glance重构 update 1

Conflicts:
	Gopkg.lock
	pkg/appsrv/appsrv.go
	pkg/cloudcommon/options.go
	pkg/compute/models/hosts.go
	pkg/compute/models/quotas.go
	pkg/compute/service/service.go
	pkg/mcclient/mcclient.go
This commit is contained in:
Qiu Jian
2018-12-26 22:52:13 +08:00
parent c70201b177
commit 2e587bd6ab
743 changed files with 314394 additions and 393 deletions

View File

@@ -164,6 +164,10 @@
name = "k8s.io/client-go"
version = "9.0.0"
[[constraint]]
branch = "master"
name = "github.com/anacrolix/torrent"
[[constraint]]
branch = "master"
name = "yunion.io/x/jsonutils"

View File

@@ -11,7 +11,7 @@ func init() {
Limit int64 `help:"Limit, default 0, i.e. no limit" default:"20"`
Offset int64 `help:"Offset, default 0, i.e. no offset"`
Region string `help:"Search by region"`
ServiceId string `help:"Search by service id"`
Service string `help:"Search by service id or name"`
Interface string `help:"Search by interface"`
}
R(&EndpointListOptions{}, "endpoint-list", "List service endpoints", func(s *mcclient.ClientSession, args *EndpointListOptions) error {
@@ -29,8 +29,16 @@ func init() {
if len(args.Region) > 0 {
query.Add(jsonutils.NewString(args.Region), "region_id")
}
if len(args.ServiceId) > 0 {
query.Add(jsonutils.NewString(args.ServiceId), "service_id")
if len(args.Service) > 0 {
srvMod, err := modules.GetModule(s, "services")
if err != nil {
return err
}
srvId, err := srvMod.GetId(s, args.Service, nil)
if err != nil {
return err
}
query.Add(jsonutils.NewString(srvId), "service_id")
}
if len(args.Interface) > 0 {
query.Add(jsonutils.NewString(args.Interface), "interface")

View File

@@ -396,4 +396,35 @@ func init() {
return nil
})
R(&ImageShowOptions{}, "image-private", "Make a image private", func(s *mcclient.ClientSession, args *ImageShowOptions) error {
if len(args.ID) == 0 {
return fmt.Errorf("No image ID provided")
} else if len(args.ID) == 1 {
result, err := modules.Images.PerformAction(s, args.ID[0], "private", nil)
if err != nil {
return err
}
printObject(result)
} else {
results := modules.Images.BatchPerformAction(s, args.ID, "private", nil)
printBatchResults(results, modules.Images.GetColumns(s))
}
return nil
})
R(&ImageShowOptions{}, "image-public", "Make a image public", func(s *mcclient.ClientSession, args *ImageShowOptions) error {
if len(args.ID) == 0 {
return fmt.Errorf("No image ID provided")
} else if len(args.ID) == 1 {
result, err := modules.Images.PerformAction(s, args.ID[0], "public", nil)
if err != nil {
return err
}
printObject(result)
} else {
results := modules.Images.BatchPerformAction(s, args.ID, "public", nil)
printBatchResults(results, modules.Images.GetColumns(s))
}
return nil
})
}

View File

@@ -7,70 +7,28 @@ import (
)
type QuotaBaseOptions struct {
Cpu int64 `help:"CPU count"`
Memory int64 `help:"Memory size in MB"`
Storage int64 `help:"Storage size in MB"`
Port int64 `help:"Internal NIC count"`
Eport int64 `help:"External NIC count"`
Eip int64 `help:"Elastic IP count"`
Bw int64 `help:"Internal bandwidth in Mbps"`
Ebw int64 `help:"External bandwidth in Mbps"`
Image int64 `help:"Template count"`
IsolatedDevice int64 `help:"Isolated device count"`
Snapshot int64 `help:"Snapshot count"`
Cpu int64 `help:"CPU count" json:"cpu:omitzero"`
Memory int64 `help:"Memory size in MB" json:"memory,omitzero"`
Storage int64 `help:"Storage size in MB" json:"storage,omitzero"`
Port int64 `help:"Internal NIC count" json:"port,omitzero"`
Eport int64 `help:"External NIC count" json:"eport,omitzero"`
Eip int64 `help:"Elastic IP count" json:"eip,omitzero"`
Bw int64 `help:"Internal bandwidth in Mbps" json:"bw,omitzero"`
Ebw int64 `help:"External bandwidth in Mbps" json:"ebw,omitzero"`
IsolatedDevice int64 `help:"Isolated device count" json:"isolated_device,omitzero"`
Snapshot int64 `help:"Snapshot count" json:"snapshot,omitzero"`
}
func quotaArgs2Params(args *QuotaBaseOptions) *jsonutils.JSONDict {
params := jsonutils.NewDict()
if args.Cpu > 0 {
params.Add(jsonutils.NewInt(args.Cpu), "cpu")
}
if args.Memory > 0 {
params.Add(jsonutils.NewInt(args.Memory), "memory")
}
if args.Storage > 0 {
params.Add(jsonutils.NewInt(args.Storage), "storage")
}
if args.Image > 0 {
params.Add(jsonutils.NewInt(args.Image), "image")
}
if args.Port > 0 {
params.Add(jsonutils.NewInt(args.Port), "port")
}
if args.Eport > 0 {
params.Add(jsonutils.NewInt(args.Eport), "eport")
}
if args.Eip > 0 {
params.Add(jsonutils.NewInt(args.Eip), "eip")
}
if args.Bw > 0 {
params.Add(jsonutils.NewInt(args.Bw), "bw")
}
if args.Ebw > 0 {
params.Add(jsonutils.NewInt(args.Ebw), "ebw")
}
if args.IsolatedDevice > 0 {
params.Add(jsonutils.NewInt(args.IsolatedDevice), "isolated_device")
}
if args.Snapshot > 0 {
params.Add(jsonutils.NewInt(args.Snapshot), "snapshot")
}
return params
type ImageQuotaBaseOptions struct {
Image int64 `help:"Template count" json:"image,omitzero"`
}
func init() {
type QuotaOptions struct {
Tenant string `help:"Tenant name of ID"`
User string `help:"User name of ID"`
}
R(&QuotaOptions{}, "quota", "Show quota for current user or tenant", func(s *mcclient.ClientSession, args *QuotaOptions) error {
params := jsonutils.NewDict()
if len(args.Tenant) > 0 {
params.Add(jsonutils.NewString(args.Tenant), "tenant")
}
if len(args.User) > 0 {
params.Add(jsonutils.NewString(args.User), "user")
}
params := jsonutils.Marshal(args)
result, err := modules.Quotas.GetQuota(s, params)
if err != nil {
return err
@@ -78,20 +36,22 @@ func init() {
printObject(result)
return nil
})
R(&QuotaOptions{}, "image-quota", "Show image quota for current user or tenant", func(s *mcclient.ClientSession, args *QuotaOptions) error {
params := jsonutils.Marshal(args)
result, err := modules.ImageQuotas.GetQuota(s, params)
if err != nil {
return err
}
printObject(result)
return nil
})
type QuotaSetOptions struct {
Tenant string `help:"Tenant name or ID to set quota"`
User string `help:"User name of ID"`
Tenant string `help:"Tenant name or ID to set quota" json:"tenant,omitempty"`
QuotaBaseOptions
}
R(&QuotaSetOptions{}, "quota-set", "Set quota for tenant", func(s *mcclient.ClientSession, args *QuotaSetOptions) error {
params := quotaArgs2Params(&args.QuotaBaseOptions)
if len(args.Tenant) > 0 {
params.Add(jsonutils.NewString(args.Tenant), "tenant")
}
if len(args.User) > 0 {
params.Add(jsonutils.NewString(args.User), "user")
}
params := jsonutils.Marshal(args)
result, e := modules.Quotas.DoQuotaSet(s, params)
if e != nil {
return e
@@ -100,13 +60,26 @@ func init() {
return nil
})
type ImageQuotaSetOptions struct {
Tenant string `help:"Tenant name or ID to set quota" json:"tenant,omitempty"`
ImageQuotaBaseOptions
}
R(&ImageQuotaSetOptions{}, "image-quota-set", "Set image quota for tenant", func(s *mcclient.ClientSession, args *ImageQuotaSetOptions) error {
params := jsonutils.Marshal(args)
result, e := modules.ImageQuotas.DoQuotaSet(s, params)
if e != nil {
return e
}
printObject(result)
return nil
})
type QuotaCheckOptions struct {
TENANT string `help:"Tenant name or ID to check quota"`
TENANT string `help:"Tenant name or ID to check quota" json:"tenant,omitempty"`
QuotaBaseOptions
}
R(&QuotaCheckOptions{}, "quota-check", "Check quota for tenant", func(s *mcclient.ClientSession, args *QuotaCheckOptions) error {
params := quotaArgs2Params(&args.QuotaBaseOptions)
params.Add(jsonutils.NewString(args.TENANT), "tenant")
params := jsonutils.Marshal(args)
result, e := modules.Quotas.DoQuotaCheck(s, params)
if e != nil {
return e
@@ -115,4 +88,18 @@ func init() {
return nil
})
type ImageQuotaCheckOptions struct {
TENANT string `help:"Tenant name or ID to check quota" json:"tenant,omitempty"`
ImageQuotaBaseOptions
}
R(&ImageQuotaCheckOptions{}, "image-quota-check", "Check quota for tenant", func(s *mcclient.ClientSession, args *ImageQuotaCheckOptions) error {
params := jsonutils.Marshal(args)
result, e := modules.ImageQuotas.DoQuotaCheck(s, params)
if e != nil {
return e
}
printObject(result)
return nil
})
}

View File

@@ -112,4 +112,20 @@ func init() {
printObject(result)
return nil
})
type ImageUsageOptions struct {
Project string `help:"check image usage of a project"`
}
R(&ImageUsageOptions{}, "image-usage", "Show general usage of images", func(s *mcclient.ClientSession, args *ImageUsageOptions) error {
params := jsonutils.NewDict()
if args.Project != "" {
params.Add(jsonutils.NewString(args.Project), "project")
}
result, err := modules.ImageUsages.GetUsage(s, params)
if err != nil {
return err
}
printObject(result)
return nil
})
}

9
cmd/glance/main.go Normal file
View File

@@ -0,0 +1,9 @@
package main
import (
"yunion.io/x/onecloud/pkg/image/service"
)
func main() {
service.StartService()
}

140
cmd/torrent/main.go Normal file
View File

@@ -0,0 +1,140 @@
package main
import (
"fmt"
"os"
"path/filepath"
"os/signal"
"path"
"syscall"
"time"
"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
"yunion.io/x/log"
"yunion.io/x/structarg"
"yunion.io/x/pkg/util/version"
"yunion.io/x/onecloud/pkg/util/torrentutils"
)
type Options struct {
structarg.BaseOptions
ROOT string `help:"Root directory to seed files"`
TORRENT string `help:"path to torrent file"`
Tracker []string `help:"Tracker urls, e.g. http://10.168.222.252:6969/announce or udp://tracker.istole.it:6969"`
}
func exitSignalHandlers(client *torrent.Client) {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
for {
log.Printf("close signal received: %+v", <-c)
client.Close()
}
}
func main() {
options := Options{}
parser, err := structarg.NewArgumentParser(&options, "torrent-srv", "bit-torrent server", "2018")
if err != nil {
log.Fatalf("%s", err)
}
parser.ParseArgs(os.Args[1:], true)
if options.Help {
fmt.Println(parser.HelpString())
return
}
if len(os.Args) <= 1 {
fmt.Print(parser.Usage())
return
}
if options.Version {
fmt.Println(version.GetJsonString())
return
}
root, err := filepath.Abs(options.ROOT)
if err != nil {
log.Fatalf("fail to get absolute path: %s", err)
}
var mi *metainfo.MetaInfo
if len(options.Tracker) > 0 {
// server mode
mi, err = torrentutils.GenerateTorrent(root, options.Tracker, options.TORRENT)
if err != nil {
log.Fatalf("fail to save torrent file %s", err)
}
} else {
// client mode, load mi from torrent file
mi, err = metainfo.LoadFromFile(options.TORRENT)
if err != nil {
log.Fatalf("fail to open torrent file %s", err)
}
}
clientConfig := torrent.NewDefaultClientConfig()
clientConfig.Debug = false
clientConfig.Seed = true
if len(options.Tracker) > 0 {
// server mode
clientConfig.DataDir = path.Dir(root)
} else {
// client mode
clientConfig.DataDir = root
}
clientConfig.DisableTrackers = false
clientConfig.DisablePEX = false
clientConfig.NoDHT = true
client, err := torrent.NewClient(clientConfig)
if err != nil {
log.Fatalf("error creating client: %s", err)
}
defer client.Close()
go exitSignalHandlers(client)
t, err := client.AddTorrent(mi)
if err != nil {
log.Fatalf("%s", err)
}
go func() {
<-t.GotInfo()
files := t.Info().Files
log.Debugf("Got Info, start download %d files", len(files))
for i := 0; i < len(files); i += 1 {
log.Debugf("%d: %s", i, files[i].Path)
}
t.DownloadAll()
}()
go func() {
<-client.Closed()
log.Debugf("client closed, exit!")
os.Exit(0)
}()
for {
if t.BytesCompleted() == t.Info().TotalLength() {
fmt.Printf("\rSeeding.............")
} else {
fmt.Printf("\rDownload: %.1f%%", float64(t.BytesCompleted())*100.0/float64(t.Info().TotalLength()))
}
time.Sleep(time.Second)
}
}

View File

@@ -17,7 +17,13 @@ type SAppParams struct {
SkipTrace bool
Params map[string]string
Path []string
Request *http.Request
Request *http.Request
Response http.ResponseWriter
OverrideResponseBodyWrapper bool
Cancel context.CancelFunc
}
func AppContextGetParams(ctx context.Context) *SAppParams {

View File

@@ -107,21 +107,25 @@ func (app *Application) AddReverseProxyHandler(prefix string, ef *proxy.SEndpoin
}
}
func (app *Application) AddHandler(method string, prefix string, handler func(context.Context, http.ResponseWriter, *http.Request)) {
app.AddHandler2(method, prefix, handler, nil, "", nil)
func (app *Application) AddHandler(method string, prefix string,
handler func(context.Context, http.ResponseWriter, *http.Request)) *SHandlerInfo {
return app.AddHandler2(method, prefix, handler, nil, "", nil)
}
func (app *Application) AddHandler2(method string, prefix string, handler func(context.Context, http.ResponseWriter, *http.Request), metadata map[string]interface{}, name string, tags map[string]string) {
func (app *Application) AddHandler2(method string, prefix string,
handler func(context.Context, http.ResponseWriter, *http.Request),
metadata map[string]interface{}, name string, tags map[string]string) *SHandlerInfo {
segs := SplitPath(prefix)
hi := newHandlerInfo(method, segs, handler, metadata, name, tags)
app.AddHandler3(hi)
return app.AddHandler3(hi)
}
func (app *Application) AddHandler3(hi *SHandlerInfo) {
func (app *Application) AddHandler3(hi *SHandlerInfo) *SHandlerInfo {
e := app.getRoot(hi.method).Add(hi.path, hi)
if e != nil {
log.Fatalf("Fail to register %s %s: %s", hi.method, hi.path, e)
}
return hi
}
type loggingResponseWriter struct {
@@ -220,6 +224,8 @@ func (app *Application) defaultHandle(w http.ResponseWriter, r *http.Request, ri
}
appParams := hand.GetAppParams(params, segs)
appParams.Request = r
appParams.Response = w
appParams.Cancel = cancel
session.Run(
func() {
if ctx.Err() == nil {
@@ -336,17 +342,26 @@ func (app *Application) ListenAndServeTLS(addr string, certFile, keyFile string)
}
}
func FetchEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (map[string]string, jsonutils.JSONObject, jsonutils.JSONObject) {
params := appctx.AppContextParams(ctx)
query, e := jsonutils.ParseQueryString(r.URL.RawQuery)
if e != nil {
log.Errorf("Parse query string %s failed: %s", r.URL.RawQuery, e)
func isJsonContentType(r *http.Request) bool {
contType := strings.ToLower(r.Header.Get("Content-Type"))
if strings.HasPrefix(contType, "application/json") {
return true
}
var body jsonutils.JSONObject = nil
if r.Method == "PUT" || r.Method == "POST" || r.Method == "DELETE" || r.Method == "PATCH" {
body, e = FetchJSON(r)
if e != nil {
log.Errorf("Fail to decode JSON request body: %s", e)
return false
}
func FetchEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (params map[string]string, query jsonutils.JSONObject, body jsonutils.JSONObject) {
var err error
params = appctx.AppContextParams(ctx)
query, err = jsonutils.ParseQueryString(r.URL.RawQuery)
if err != nil {
log.Errorf("Parse query string %s failed: %s", r.URL.RawQuery, err)
}
//var body jsonutils.JSONObject = nil
if (r.Method == "PUT" || r.Method == "POST" || r.Method == "DELETE" || r.Method == "PATCH") && r.ContentLength > 0 && isJsonContentType(r) {
body, err = FetchJSON(r)
if err != nil {
log.Errorf("Fail to decode JSON request body: %s", err)
}
}
return params, query, body

View File

@@ -19,66 +19,82 @@ func AddModelDispatcher(prefix string, app *appsrv.Application, manager IModelDi
metadata := map[string]interface{}{"manager": manager}
tags := map[string]string{"resource": manager.KeywordPlural()}
// list
app.AddHandler2("GET",
h := app.AddHandler2("GET",
fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
manager.Filter(listHandler), metadata, "list", tags)
manager.CustomizeHandlerInfo(h)
ctxs := manager.ContextKeywordPlural()
// list in context
if ctxs != nil && len(ctxs) > 0 {
for _, ctx := range ctxs {
app.AddHandler2("GET",
h = app.AddHandler2("GET",
fmt.Sprintf("%s/%s/<resid>/%s", prefix, ctx, manager.KeywordPlural()),
manager.Filter(listInContextHandler), metadata, "list", tags)
manager.Filter(listInContextHandler), metadata, fmt.Sprintf("list_in_%s", ctx), tags)
manager.CustomizeHandlerInfo(h)
}
}
// Head
h = app.AddHandler2("HEAD",
fmt.Sprintf("%s/%s/<resid>", prefix, manager.KeywordPlural()),
manager.Filter(headHandler), metadata, "head_details", tags)
manager.CustomizeHandlerInfo(h)
// Get
app.AddHandler2("GET",
h = app.AddHandler2("GET",
fmt.Sprintf("%s/%s/<resid>", prefix, manager.KeywordPlural()),
manager.Filter(getHandler), metadata, "get_details", tags)
manager.CustomizeHandlerInfo(h)
// get spec
app.AddHandler2("GET",
h = app.AddHandler2("GET",
fmt.Sprintf("%s/%s/<resid>/<spec>", prefix, manager.KeywordPlural()),
manager.Filter(getSpecHandler), metadata, "get_specific", tags)
manager.CustomizeHandlerInfo(h)
// create
// create multi
app.AddHandler2("POST",
h = app.AddHandler2("POST",
fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
manager.Filter(createHandler), metadata, "create", tags)
manager.CustomizeHandlerInfo(h)
// create in context
if ctxs != nil && len(ctxs) > 0 {
for _, ctx := range ctxs {
app.AddHandler2("POST",
h = app.AddHandler2("POST",
fmt.Sprintf("%s/%s/<resid>/%s", prefix, ctx, manager.KeywordPlural()),
manager.Filter(createInContextHandler), metadata, "create", tags)
manager.Filter(createInContextHandler), metadata, fmt.Sprintf("create_in_%s", ctx), tags)
manager.CustomizeHandlerInfo(h)
}
}
// batchPerformAction
app.AddHandler2("POST",
h = app.AddHandler2("POST",
fmt.Sprintf("%s/%s/<action>", prefix, manager.KeywordPlural()),
manager.Filter(performClassActionHandler), metadata, "perform_class_action", tags)
manager.CustomizeHandlerInfo(h)
// performAction
// create in context
app.AddHandler2("POST",
h = app.AddHandler2("POST",
fmt.Sprintf("%s/%s/<resid>/<action>", prefix, manager.KeywordPlural()),
manager.Filter(performActionHandler), metadata, "perform_action", tags)
manager.CustomizeHandlerInfo(h)
// batchUpdate
/* app.AddHandler2("PUT",
fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
manager.Filter(updateClassHandler), metadata, "update_class", tags)
*/
// update
app.AddHandler2("PUT",
h = app.AddHandler2("PUT",
fmt.Sprintf("%s/%s/<resid>", prefix, manager.KeywordPlural()),
manager.Filter(updateHandler), metadata, "update", tags)
manager.CustomizeHandlerInfo(h)
// batch Delete
/* app.AddHandler2("DELTE",
fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()),
manager.Filter(batachDeleteHandler), metadata, "batch_delete", tags)
*/
// Delete
app.AddHandler2("DELETE",
h = app.AddHandler2("DELETE",
fmt.Sprintf("%s/%s/<resid>", prefix, manager.KeywordPlural()),
manager.Filter(deleteHandler), metadata, "delete", tags)
manager.CustomizeHandlerInfo(h)
}
func fetchEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (IModelDispatchHandler, map[string]string, jsonutils.JSONObject, jsonutils.JSONObject) {
@@ -134,14 +150,38 @@ func wrapBody(body jsonutils.JSONObject, key string) jsonutils.JSONObject {
}
}
func headHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
defer func() {
w.Header().Set("Content-Length", "0")
w.Write([]byte{})
}()
manager, params, query, _ := fetchEnv(ctx, w, r)
_, err := manager.Get(ctx, params["<resid>"], mergeQueryParams(params, query, "<resid>"), true)
if err != nil {
jsonErr := httperrors.NewGeneralError(err)
httperrors.SendHTTPErrorHeader(w, jsonErr.Code)
return
}
}
func getHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
manager, params, query, _ := fetchEnv(ctx, w, r)
result, err := manager.Get(ctx, params["<resid>"], mergeQueryParams(params, query, "<resid>"))
result, err := manager.Get(ctx, params["<resid>"], mergeQueryParams(params, query, "<resid>"), false)
if err != nil {
httperrors.GeneralServerError(w, err)
return
}
appsrv.SendJSON(w, wrapBody(result, manager.Keyword()))
if result != nil {
appParams := appsrv.AppContextGetParams(ctx)
var body jsonutils.JSONObject
if appParams != nil && appParams.OverrideResponseBodyWrapper {
body = result
} else {
body = wrapBody(result, manager.Keyword())
}
appsrv.SendJSON(w, body)
}
}
func getSpecHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
@@ -151,21 +191,35 @@ func getSpecHandler(ctx context.Context, w http.ResponseWriter, r *http.Request)
httperrors.GeneralServerError(w, err)
return
}
appsrv.SendJSON(w, wrapBody(result, manager.Keyword()))
if result != nil {
appsrv.SendJSON(w, wrapBody(result, manager.Keyword()))
}
}
func createHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
manager, params, query, body := fetchEnv(ctx, w, r)
handleCreate(ctx, w, manager, "", mergeQueryParams(params, query), body)
handleCreate(ctx, w, manager, "", mergeQueryParams(params, query), body, r)
}
func handleCreate(ctx context.Context, w http.ResponseWriter, manager IModelDispatchHandler, ctxId string, query jsonutils.JSONObject, body jsonutils.JSONObject) {
count, _ := body.Int("count")
data, err := body.Get(manager.Keyword())
if err != nil {
httperrors.InvalidInputError(w,
fmt.Sprintf("No request key: %s", manager.Keyword()))
return
func handleCreate(ctx context.Context, w http.ResponseWriter, manager IModelDispatchHandler, ctxId string, query jsonutils.JSONObject, body jsonutils.JSONObject, r *http.Request) {
count := int64(1)
var data jsonutils.JSONObject
var err error
if body != nil {
count, _ = body.Int("count")
data, err = body.Get(manager.Keyword())
if err != nil {
httperrors.InvalidInputError(w,
fmt.Sprintf("No request key: %s", manager.Keyword()))
return
}
} else {
data, err = manager.FetchCreateHeaderData(ctx, r.Header)
if err != nil {
httperrors.InvalidInputError(w,
fmt.Sprintf("In valid request header: %s", err))
return
}
}
if count <= 1 {
result, err := manager.Create(ctx, query, data, ctxId)
@@ -194,7 +248,7 @@ func handleCreate(ctx context.Context, w http.ResponseWriter, manager IModelDisp
func createInContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
manager, params, query, body := fetchEnv(ctx, w, r)
ctxId := params["<resid>"]
handleCreate(ctx, w, manager, ctxId, mergeQueryParams(params, query, "<resid>"), body)
handleCreate(ctx, w, manager, ctxId, mergeQueryParams(params, query, "<resid>"), body, r)
}
func performClassActionHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
@@ -258,11 +312,22 @@ func updateClassHandler(ctx context.Context, w http.ResponseWriter, r *http.Requ
func updateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
manager, params, query, body := fetchEnv(ctx, w, r)
data, err := body.Get(manager.Keyword())
if err != nil {
httperrors.InvalidInputError(w,
fmt.Sprintf("No request key: %s", manager.Keyword()))
return
var data jsonutils.JSONObject
var err error
if body != nil {
data, err = body.Get(manager.Keyword())
if err != nil {
httperrors.InvalidInputError(w,
fmt.Sprintf("No request key: %s", manager.Keyword()))
return
}
} else {
data, err = manager.FetchUpdateHeaderData(ctx, r.Header)
if err != nil {
httperrors.InvalidInputError(w,
fmt.Sprintf("In valid request header: %s", err))
return
}
}
result, err := manager.Update(ctx, params["<resid>"], mergeQueryParams(params, query, "<resid>"), data)
if err != nil {

View File

@@ -3,6 +3,7 @@ package dispatcher
import (
"context"
"net/http"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/mcclient/modules"
@@ -20,7 +21,7 @@ type IModelDispatchHandler interface {
ContextKeywordPlural() []string
List(ctx context.Context, query jsonutils.JSONObject, ctxId string) (*modules.ListResult, error)
Get(ctx context.Context, idstr string, query jsonutils.JSONObject) (jsonutils.JSONObject, error)
Get(ctx context.Context, idstr string, query jsonutils.JSONObject, isHead bool) (jsonutils.JSONObject, error)
GetSpecific(ctx context.Context, idstr string, spec string, query jsonutils.JSONObject) (jsonutils.JSONObject, error)
Create(ctx context.Context, query jsonutils.JSONObject, data jsonutils.JSONObject, ctxId string) (jsonutils.JSONObject, error)
BatchCreate(ctx context.Context, query jsonutils.JSONObject, data jsonutils.JSONObject, count int, ctxId string) ([]modules.SubmitResult, error)
@@ -30,6 +31,10 @@ type IModelDispatchHandler interface {
Update(ctx context.Context, idstr string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error)
// DeleteClass(ctx context.Context, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error)
Delete(ctx context.Context, idstr string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error)
CustomizeHandlerInfo(info *appsrv.SHandlerInfo)
FetchCreateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error)
FetchUpdateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error)
}
type IJointModelDispatchHandler interface {

View File

@@ -14,6 +14,7 @@ import (
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
"net/http"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
@@ -64,6 +65,10 @@ func (dispatcher *DBModelDispatcher) Filter(f appsrv.FilterHandler) appsrv.Filte
}
}
func (dispatcher *DBModelDispatcher) CustomizeHandlerInfo(handler *appsrv.SHandlerInfo) {
dispatcher.modelManager.CustomizeHandlerInfo(handler)
}
func fetchUserCredential(ctx context.Context) mcclient.TokenCredential {
token := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential)
if token == nil && !consts.IsRbacEnabled() {
@@ -130,7 +135,7 @@ func searchFields(manager IModelManager, userCred mcclient.TokenCredential) []st
return ret
}
func getDetailFields(manager IModelManager, userCred mcclient.TokenCredential) []string {
func GetDetailFields(manager IModelManager, userCred mcclient.TokenCredential) []string {
ret := make([]string, 0)
for _, col := range manager.TableSpec().Columns() {
tags := col.Tags()
@@ -407,7 +412,7 @@ func fetchContextObjectId(manager IModelManager, ctx context.Context, userCred m
}
}
func listItems(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, ctxId string) (*modules.ListResult, error) {
func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, ctxId string) (*modules.ListResult, error) {
var maxLimit int64 = 2048
limit, _ := query.Int("limit")
offset, _ := query.Int("offset")
@@ -539,7 +544,7 @@ func (dispatcher *DBModelDispatcher) List(ctx context.Context, query jsonutils.J
return nil, httperrors.NewForbiddenError("Not allow to list")
}
items, err := listItems(dispatcher.modelManager, ctx, userCred, query, ctxId)
items, err := ListItems(dispatcher.modelManager, ctx, userCred, query, ctxId)
if err != nil {
log.Errorf("Fail to list items: %s", err)
return nil, httperrors.NewGeneralError(err)
@@ -563,19 +568,48 @@ func getModelExtraDetails(item IModel, ctx context.Context, extra *jsonutils.JSO
return extra
}
func getModelItemDetails(manager IModelManager, item IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isHead bool) (jsonutils.JSONObject, error) {
appParams := appsrv.AppContextGetParams(ctx)
if appParams == nil && isHead {
log.Errorf("fail to get http response writer???")
return nil, httperrors.NewInternalServerError("fail to get http response writer from context")
}
hdrs := item.GetExtraDetailsHeaders(ctx, userCred, query)
for k, v := range hdrs {
appParams.Response.Header().Add(k, v)
}
if isHead {
appParams.Response.Header().Add("Content-Length", "0")
appParams.Response.Write([]byte{})
return nil, nil
}
if manager.IsCustomizedGetDetailsBody() {
return item.CustomizedGetDetailsBody(ctx, userCred, query)
} else {
return getItemDetails(manager, item, ctx, userCred, query)
}
}
func getItemDetails(manager IModelManager, item IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
jsonData := jsonutils.Marshal(item)
jsonDict, ok := jsonData.(*jsonutils.JSONDict)
if !ok {
return nil, fmt.Errorf("fail to convert model to json")
}
jsonDict = jsonDict.CopyIncludes(getDetailFields(manager, userCred)...)
extraDict := item.GetExtraDetails(ctx, userCred, query)
if extraDict != nil {
extraDict, err := item.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, httperrors.NewGeneralError(err)
} else if extraDict != nil {
jsonData := jsonutils.Marshal(item)
jsonDict, ok := jsonData.(*jsonutils.JSONDict)
if !ok {
return nil, fmt.Errorf("fail to convert model to json")
}
jsonDict = jsonDict.CopyIncludes(GetDetailFields(manager, userCred)...)
jsonDict.Update(extraDict)
jsonDict = getModelExtraDetails(item, ctx, jsonDict)
return jsonDict, nil
} else {
// override GetExtraDetails
return nil, nil
}
jsonDict = getModelExtraDetails(item, ctx, jsonDict)
return jsonDict, nil
}
func (dispatcher *DBModelDispatcher) tryGetModelProperty(ctx context.Context, property string, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
@@ -620,7 +654,7 @@ func (dispatcher *DBModelDispatcher) tryGetModelProperty(ctx context.Context, pr
}
}
func (dispatcher *DBModelDispatcher) Get(ctx context.Context, idStr string, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
func (dispatcher *DBModelDispatcher) Get(ctx context.Context, idStr string, query jsonutils.JSONObject, isHead bool) (jsonutils.JSONObject, error) {
// log.Debugf("Get %s", idStr)
userCred := fetchUserCredential(ctx)
@@ -647,7 +681,7 @@ func (dispatcher *DBModelDispatcher) Get(ctx context.Context, idStr string, quer
if !isAllow {
return nil, httperrors.NewForbiddenError("Not allow to get details")
}
return getItemDetails(dispatcher.modelManager, model, ctx, userCred, query)
return getModelItemDetails(dispatcher.modelManager, model, ctx, userCred, query, isHead)
}
func (dispatcher *DBModelDispatcher) GetSpecific(ctx context.Context, idStr string, spec string, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
@@ -820,10 +854,13 @@ func doCreateItem(manager IModelManager, ctx context.Context, userCred mcclient.
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
model.PostCreate(ctx, userCred, ownerProjId, query, dataDict)
return model, nil
}
func (dispatcher *DBModelDispatcher) FetchCreateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
return dispatcher.modelManager.FetchCreateHeaderData(ctx, header)
}
func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils.JSONObject, data jsonutils.JSONObject, ctxId string) (jsonutils.JSONObject, error) {
userCred := fetchUserCredential(ctx)
@@ -845,9 +882,6 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
}
}
lockman.LockClass(ctx, dispatcher.modelManager, ownerProjId)
defer lockman.ReleaseClass(ctx, dispatcher.modelManager, ownerProjId)
var isAllow bool
if consts.IsRbacEnabled() {
isAllow = isClassActionRbacAllowed(dispatcher.modelManager, userCred, ownerProjId, policy.PolicyActionCreate)
@@ -858,11 +892,23 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
return nil, httperrors.NewForbiddenError("Not allow to create item")
}
model, err := doCreateItem(dispatcher.modelManager, ctx, userCred, ownerProjId, query, data)
model, err := func() (IModel, error) {
lockman.LockClass(ctx, dispatcher.modelManager, ownerProjId)
defer lockman.ReleaseClass(ctx, dispatcher.modelManager, ownerProjId)
return doCreateItem(dispatcher.modelManager, ctx, userCred, ownerProjId, query, data)
}()
if err != nil {
log.Errorf("fail to doCreateItem %s", err)
return nil, httperrors.NewGeneralError(err)
}
lockman.LockObject(ctx, model)
defer lockman.ReleaseObject(ctx, model)
model.PostCreate(ctx, userCred, ownerProjId, query, data)
OpsLog.LogEvent(model, ACT_CREATE, model.GetShortDesc(ctx), userCred)
logclient.AddActionLog(model, logclient.ACT_CREATE, "", userCred, true)
dispatcher.modelManager.OnCreateComplete(ctx, []IModel{model}, userCred, query, data)
@@ -909,9 +955,6 @@ func (dispatcher *DBModelDispatcher) BatchCreate(ctx context.Context, query json
}
}
lockman.LockClass(ctx, dispatcher.modelManager, ownerProjId)
defer lockman.ReleaseClass(ctx, dispatcher.modelManager, ownerProjId)
var isAllow bool
if consts.IsRbacEnabled() {
isAllow = isClassActionRbacAllowed(dispatcher.modelManager, userCred, ownerProjId, policy.PolicyActionCreate)
@@ -922,27 +965,53 @@ func (dispatcher *DBModelDispatcher) BatchCreate(ctx context.Context, query json
return nil, httperrors.NewForbiddenError("Not allow to create item")
}
multiData, err := expandMultiCreateParams(data, count)
type sCreateResult struct {
model IModel
err error
}
var multiData []jsonutils.JSONObject
createResults, err := func() ([]sCreateResult, error) {
lockman.LockClass(ctx, dispatcher.modelManager, ownerProjId)
defer lockman.ReleaseClass(ctx, dispatcher.modelManager, ownerProjId)
multiData, err = expandMultiCreateParams(data, count)
if err != nil {
return nil, err
}
ret := make([]sCreateResult, len(multiData))
for i, cdata := range multiData {
model, err := doCreateItem(dispatcher.modelManager, ctx, userCred, ownerProjId, query, cdata)
ret[i] = sCreateResult{model: model, err: err}
}
return ret, nil
}()
if err != nil {
return nil, err
}
results := make([]modules.SubmitResult, count)
models := make([]IModel, 0)
for i, cdata := range multiData {
model, err := doCreateItem(dispatcher.modelManager, ctx, userCred, ownerProjId, query, cdata)
for i, res := range createResults {
result := modules.SubmitResult{}
if err != nil {
jsonErr, ok := err.(*httputils.JSONClientError)
if res.err != nil {
jsonErr, ok := res.err.(*httputils.JSONClientError)
if ok {
result.Status = jsonErr.Code
result.Data = jsonutils.Marshal(jsonErr)
} else {
result.Status = 500
result.Data = jsonutils.NewString(err.Error())
result.Data = jsonutils.NewString(res.err.Error())
}
} else {
models = append(models, model)
body, err := getItemDetails(dispatcher.modelManager, model, ctx, userCred, query)
lockman.LockObject(ctx, res.model)
defer lockman.ReleaseObject(ctx, res.model)
res.model.PostCreate(ctx, userCred, ownerProjId, query, data)
models = append(models, res.model)
body, err := getItemDetails(dispatcher.modelManager, res.model, ctx, userCred, query)
if err != nil {
result.Status = 500
result.Data = jsonutils.NewString(err.Error())
@@ -954,6 +1023,9 @@ func (dispatcher *DBModelDispatcher) BatchCreate(ctx context.Context, query json
results[i] = result
}
if len(models) > 0 {
lockman.LockClass(ctx, dispatcher.modelManager, ownerProjId)
defer lockman.ReleaseClass(ctx, dispatcher.modelManager, ownerProjId)
dispatcher.modelManager.OnCreateComplete(ctx, models, userCred, query, multiData[0])
}
return results, nil
@@ -1145,7 +1217,9 @@ func updateItem(manager IModelManager, item IModel, ctx context.Context, userCre
logclient.AddActionLog(item, logclient.ACT_UPDATE, errMsg, userCred, false)
return nil, httperrors.NewGeneralError(err)
}
item.PreUpdate(ctx, userCred, query, dataDict)
diff, err := manager.TableSpec().Update(item, func() error {
filterData := dataDict.CopyIncludes(updateFields(manager, userCred)...)
err = filterData.Unmarshal(item)
@@ -1172,9 +1246,16 @@ func updateItem(manager IModelManager, item IModel, ctx context.Context, userCre
} else {
logclient.AddActionLog(item, logclient.ACT_UPDATE, "", userCred, true)
}
item.PostUpdate(ctx, userCred, query, data)
return getItemDetails(manager, item, ctx, userCred, query)
}
func (dispatcher *DBModelDispatcher) FetchUpdateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
return dispatcher.modelManager.FetchUpdateHeaderData(ctx, header)
}
func (dispatcher *DBModelDispatcher) Update(ctx context.Context, idStr string, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
userCred := fetchUserCredential(ctx)
model, err := fetchItem(dispatcher.modelManager, ctx, userCred, idStr, nil)

View File

@@ -109,7 +109,7 @@ func (dispatcher *DBJointModelDispatcher) _listJoint(ctx context.Context, userCr
return nil, httperrors.NewForbiddenError("Not allow to list")
}
items, err := listItems(dispatcher.JointModelManager(), ctx, userCred, queryDict, "")
items, err := ListItems(dispatcher.JointModelManager(), ctx, userCred, queryDict, "")
if err != nil {
log.Errorf("Fail to list items: %s", err)
return nil, httperrors.NewGeneralError(err)

View File

@@ -7,6 +7,8 @@ import (
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/sqlchemy"
"net/http"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
)
@@ -63,6 +65,11 @@ type IModelManager interface {
DoCreate(ctx context.Context, userCred mcclient.TokenCredential, kwargs jsonutils.JSONObject, data jsonutils.JSONObject, realManager IModelManager) (IModel, error)
InitializeData() error
CustomizeHandlerInfo(info *appsrv.SHandlerInfo)
FetchCreateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error)
FetchUpdateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error)
IsCustomizedGetDetailsBody() bool
}
type IModel interface {
@@ -82,7 +89,8 @@ type IModel interface {
// get hooks
AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool
GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict
GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error)
GetExtraDetailsHeaders(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) map[string]string
// create hooks
CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) error
@@ -110,6 +118,8 @@ type IModel interface {
PostDelete(ctx context.Context, userCred mcclient.TokenCredential)
GetOwnerProjectId() string
CustomizedGetDetailsBody(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error)
}
type IResourceModelManager interface {

View File

@@ -5,7 +5,9 @@ import (
"database/sql"
"fmt"
"net/http"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/sqlchemy"
)
@@ -170,6 +172,22 @@ func (manager *SModelBaseManager) GetExportExtraKeys(ctx context.Context, query
return jsonutils.NewDict()
}
func (manager *SModelBaseManager) CustomizeHandlerInfo(info *appsrv.SHandlerInfo) {
// do nothing
}
func (manager *SModelBaseManager) FetchCreateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
return nil, nil
}
func (manager *SModelBaseManager) FetchUpdateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
return nil, nil
}
func (manager *SModelBaseManager) IsCustomizedGetDetailsBody() bool {
return false
}
func (model *SModelBase) GetId() string {
return ""
}
@@ -210,8 +228,12 @@ func (model *SModelBase) AllowGetDetails(ctx context.Context, userCred mcclient.
return false
}
func (model *SModelBase) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
return jsonutils.NewDict()
func (model *SModelBase) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
return jsonutils.NewDict(), nil
}
func (model *SModelBase) GetExtraDetailsHeaders(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) map[string]string {
return nil
}
// create hooks
@@ -286,3 +308,7 @@ func (model *SModelBase) Delete(ctx context.Context, userCred mcclient.TokenCred
func (model *SModelBase) GetOwnerProjectId() string {
return ""
}
func (model *SModelBase) CustomizedGetDetailsBody(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return nil, nil
}

View File

@@ -25,23 +25,23 @@ func AddQuotaHandler(manager *SQuotaManager, prefix string, app *appsrv.Applicat
_manager = manager
app.AddHandler2("GET",
fmt.Sprintf("%s/quotas", prefix),
fmt.Sprintf("%s/%s", prefix, _manager.Keyword()),
auth.Authenticate(getQuotaHanlder), nil, "get_quota", nil)
app.AddHandler2("GET",
fmt.Sprintf("%s/quotas/<tenantid>", prefix),
fmt.Sprintf("%s/%s/<tenantid>", prefix, _manager.Keyword()),
auth.Authenticate(getQuotaHanlder), nil, "get_quota", nil)
app.AddHandler2("POST",
fmt.Sprintf("%s/quotas", prefix),
fmt.Sprintf("%s/%s", prefix, _manager.Keyword()),
auth.Authenticate(setQuotaHanlder), nil, "set_quota", nil)
app.AddHandler2("POST",
fmt.Sprintf("%s/quotas/<tenantid>", prefix),
fmt.Sprintf("%s/%s/<tenantid>", prefix, _manager.Keyword()),
auth.Authenticate(setQuotaHanlder), nil, "set_quota", nil)
app.AddHandler2("POST",
fmt.Sprintf("%s/quotas/<tenantid>/<action>", prefix),
fmt.Sprintf("%s/%s/<tenantid>/<action>", prefix, _manager.Keyword()),
auth.Authenticate(checkQuotaHanlder), nil, "check_quota", nil)
}
@@ -80,7 +80,7 @@ func getQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request
projectId = userCred.GetProjectId()
if consts.IsRbacEnabled() {
result := policy.PolicyManager.Allow(false, userCred, consts.GetServiceType(),
"quotas", policy.PolicyActionGet)
_manager.Keyword(), policy.PolicyActionGet)
if result == rbacutils.Deny {
httperrors.ForbiddenError(w, "not allow to get quota")
return
@@ -101,7 +101,7 @@ func getQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request
}
if consts.IsRbacEnabled() {
if policy.PolicyManager.Allow(true, userCred, consts.GetServiceType(),
"quotas", policy.PolicyActionGet) != rbacutils.AdminAllow {
_manager.Keyword(), policy.PolicyActionGet) != rbacutils.AdminAllow {
httperrors.ForbiddenError(w, "not allow to query quota")
return
}
@@ -127,7 +127,7 @@ func getQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request
}
body := jsonutils.NewDict()
body.Add(quota, "quotas")
body.Add(quota, _manager.Keyword())
appsrv.SendJSON(w, body)
}
@@ -138,10 +138,10 @@ func setQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request
var isAllow bool
if consts.IsRbacEnabled() {
isAllow = policy.PolicyManager.Allow(true, userCred, consts.GetServiceType(),
"quotas", policy.PolicyActionUpdate) == rbacutils.AdminAllow
_manager.Keyword(), policy.PolicyActionUpdate) == rbacutils.AdminAllow
} else {
isAllow = userCred.IsAdminAllow(consts.GetServiceType(),
"quotas", policy.PolicyActionUpdate)
_manager.Keyword(), policy.PolicyActionUpdate)
}
if !isAllow {
httperrors.ForbiddenError(w, "not allow to set quota")
@@ -171,7 +171,7 @@ func setQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request
return
}
quota := _manager.newQuota()
err = body.Unmarshal(quota, "quotas")
err = body.Unmarshal(quota, _manager.Keyword())
if err != nil {
log.Errorf("Fail to decode JSON request body: %s", err)
httperrors.InvalidInputError(w, "fail to decode body")
@@ -192,7 +192,7 @@ func setQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request
return
}
rbody := jsonutils.NewDict()
rbody.Add(oquota.ToJSON(""), "quotas")
rbody.Add(oquota.ToJSON(""), _manager.Keyword())
appsrv.SendJSON(w, rbody)
}
@@ -213,7 +213,7 @@ func checkQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Reque
}
if consts.IsRbacEnabled() {
if policy.PolicyManager.Allow(true, userCred, consts.GetServiceType(),
"quotas", policy.PolicyActionGet) != rbacutils.AdminAllow {
_manager.Keyword(), policy.PolicyActionGet) != rbacutils.AdminAllow {
httperrors.ForbiddenError(w, "not allow to query quota")
return
}
@@ -238,7 +238,7 @@ func checkQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Reque
}
body, err := appsrv.FetchJSON(r)
quota := _manager.newQuota()
err = body.Unmarshal(quota, "quotas")
err = body.Unmarshal(quota, _manager.Keyword())
if err != nil {
log.Errorf("Fail to decode JSON request body: %s", err)
httperrors.InvalidInputError(w, "fail to decode body")
@@ -250,6 +250,6 @@ func checkQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Reque
return
}
rbody := jsonutils.NewDict()
rbody.Add(used.ToJSON(""), "quotas")
rbody.Add(used.ToJSON(""), _manager.Keyword())
appsrv.SendJSON(w, rbody)
}

View File

@@ -0,0 +1,19 @@
package quotas
import "fmt"
func NonNegative(val int) int {
if val < 0 {
return 0
} else {
return val
}
}
func KeyName(prefix, name string) string {
if len(prefix) > 0 {
return fmt.Sprintf("%s.%s", prefix, name)
} else {
return name
}
}

View File

@@ -17,7 +17,7 @@ type SResourceBase struct {
UpdatedAt time.Time `nullable:"false" updated_at:"true" list:"user"`
UpdateVersion int `default:"0" nullable:"false" auto_version:"true" list:"user"`
DeletedAt time.Time ``
Deleted bool `nullable:"false" default:"false"`
Deleted bool `nullable:"false" default:"false" index:"true"`
}
type SResourceBaseManager struct {

View File

@@ -11,7 +11,7 @@ import (
type SSharableVirtualResourceBase struct {
SVirtualResourceBase
IsPublic bool `default:"false" nullable:"false" create:"admin_optional" list:"user"`
IsPublic bool `default:"false" nullable:"false" index:"true" create:"admin_optional" list:"user"`
}
type SSharableVirtualResourceBaseManager struct {

View File

@@ -5,11 +5,12 @@ import (
"database/sql"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
type STenantCacheManager struct {
@@ -127,3 +128,14 @@ func (manager *STenantCacheManager) Save(ctx context.Context, idStr string, name
}
}
}
func (manager *STenantCacheManager) GenerateProjectUserCred(ctx context.Context, projectName string) (mcclient.TokenCredential, error) {
project, err := manager.FetchTenantByIdOrName(ctx, projectName)
if err != nil {
return nil, err
}
return &mcclient.SSimpleToken{
Project: project.Name,
ProjectId: project.Id,
}, nil
}

View File

@@ -191,9 +191,12 @@ func (model *SVirtualResourceBase) GetCustomizeColumns(ctx context.Context, user
return model.getMoreDetails(ctx, userCred, query, extra)
}
func (model *SVirtualResourceBase) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := model.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return model.getMoreDetails(ctx, userCred, query, extra)
func (model *SVirtualResourceBase) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := model.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return model.getMoreDetails(ctx, userCred, query, extra), nil
}
func (model *SVirtualResourceBase) AllowPerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {

View File

@@ -5,11 +5,11 @@ import (
"errors"
"time"
"go.etcd.io/etcd/clientv3"
"crypto/tls"
"fmt"
"go.etcd.io/etcd/clientv3"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/util/seclib2"
"fmt"
)
var (
@@ -176,7 +176,7 @@ func (cli *SEtcdClient) Get(ctx context.Context, key string) ([]byte, error) {
}
type SEtcdKeyValue struct {
Key string
Key string
Value []byte
}
@@ -194,7 +194,7 @@ func (cli *SEtcdClient) List(ctx context.Context, prefix string) ([]SEtcdKeyValu
ret := make([]SEtcdKeyValue, len(resp.Kvs))
for i := 0; i < len(resp.Kvs); i += 1 {
ret[i] = SEtcdKeyValue{
Key: string(resp.Kvs[i].Key[len(cli.namespace):]),
Key: string(resp.Kvs[i].Key[len(cli.namespace):]),
Value: resp.Kvs[i].Value,
}
}

View File

@@ -13,17 +13,17 @@ import (
)
type CommonOptions struct {
Port int `help:"The port that the service runs on"`
Address string `help:"The IP address to serve on (set to 0.0.0.0 for all interfaces)" default:"0.0.0.0"`
Port int `help:"The port that the service runs on" alias:"bind-port"`
Address string `help:"The IP address to serve on (set to 0.0.0.0 for all interfaces)" default:"0.0.0.0" alias:"bind-host"`
LogLevel string `help:"log level" default:"info" choices:"debug|info|warn|error"`
LogVerboseLevel int `help:"log verbosity level" default:"0"`
Region string `help:"Region name or ID"`
Region string `help:"Region name or ID" alias:"auth-region"`
AuthURL string `help:"Keystone auth URL" alias:"auth-uri"`
AdminUser string `help:"Admin username"`
AdminDomain string `help:"Admin user domain"`
AdminPassword string `help:"Admin password"`
AdminPassword string `help:"Admin password" alias:"admin-passwd"`
AdminProject string `help:"Admin project" default:"system" alias:"admin-tenant-name"`
CorsHosts []string `help:"List of hostname that allow CORS"`
AuthTokenCacheSize uint32 `help:"Auth token Cache Size" default:"2048"`

View File

@@ -0,0 +1,8 @@
package pending_delete
type SPendingDeleteOptions struct {
EnablePendingDelete bool `default:"true" help:"Turn on/off pending-delete resource, default is on" alias:"delayed_delete"`
PendingDeleteCheckSeconds int `default:"3600" help:"How long to wait to scan pending-delete resource, default is 1 hour"`
PendingDeleteExpireSeconds int `default:"259200" help:"How long a pending-delete resource cleaned automatically, default 3 days" alias:"scrub_time"`
PendingDeleteMaxCleanBatchSize int `default:"50" help:"How many pending-delete items can be clean in a batch"`
}

View File

@@ -0,0 +1,20 @@
package qemuimg
type TImageFormat string
const (
QCOW2 = TImageFormat("qcow2")
VMDK = TImageFormat("vmdk")
VHD = TImageFormat("vhd")
ISO = TImageFormat("iso")
RAW = TImageFormat("raw")
)
func (fmt TImageFormat) String() string {
switch string(fmt) {
case "vhd":
return "vpc"
default:
return string(fmt)
}
}

View File

@@ -0,0 +1,56 @@
package qemuimg
import (
"os/exec"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/qemutils"
"regexp"
"fmt"
"yunion.io/x/onecloud/pkg/cloudcommon/version"
)
const (
qemuImgVersionPattern = `qemu-img version (?P<ver>\d+\.\d+(\.\d+)?)`
)
var (
qemuImgVersionRegexp = regexp.MustCompile(qemuImgVersionPattern)
qemuImgVersion string
)
func getQemuImgVersion() string {
cmd := exec.Command(qemutils.GetQemuImg(), "--version")
out, err := cmd.CombinedOutput()
if err != nil {
log.Errorf("check qemu-img version fail %s", err)
return ""
}
matches := qemuImgVersionRegexp.FindStringSubmatch(string(out))
if len(matches) > 1 {
return matches[1]
}
return ""
}
func QemuImgInit() error {
ver := getQemuImgVersion()
if len(ver) == 0 {
return fmt.Errorf("fail to find qemu-img")
}
qemuImgVersion = ver
return nil
}
func qcow2SparseOptions() []string {
if version.LE(qemuImgVersion, "1.1") {
return []string {"preallocation=metadata", "cluster_size=2M"}
} else if version.LE(qemuImgVersion, "1.7.1") {
return []string {"preallocation=metadata", "lazy_refcounts=on"}
} else if version.LE(qemuImgVersion, "2.2") {
return []string {"preallocation=metadata", "lazy_refcounts=on", "cluster_size=2M"}
} else {
return []string {}
}
}

View File

@@ -0,0 +1,417 @@
package qemuimg
import (
"strings"
"os"
"os/exec"
"fmt"
"bytes"
"io"
"strconv"
"errors"
"yunion.io/x/pkg/utils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/cloudcommon/qemutils"
"yunion.io/x/onecloud/pkg/util/fileutils2"
)
var (
ErrUnsupportedFormat = errors.New("unsupported format")
)
type SQemuImage struct {
Path string
Password string
Format TImageFormat
SizeBytes int64
ActualSizeBytes int64
ClusterSize int
BackFilePath string
Compat string
Encryption bool
Subformat string
}
func NewQemuImage(path string) (*SQemuImage, error) {
return NewEncryptedQemuImage(path, "")
}
func NewEncryptedQemuImage(path string, password string) (*SQemuImage, error) {
qemuImg := SQemuImage{Path: path, Password: password}
err := qemuImg.parse()
if err != nil {
return nil, err
}
return &qemuImg, nil
}
func (img *SQemuImage) parse() error {
if strings.HasPrefix(img.Path, "nbd") {
// nbd TCP -> nbd:<server-ip>:<port>
// nbd Unix Domain Sockets -> nbd:unix:<domain-socket-file>
img.ActualSizeBytes = 0
} else if strings.HasPrefix(img.Path, "iscsi") {
// iSCSI LUN -> iscsi://<target-ip>[:<port>]/<target-iqn>/<lun>
return cloudprovider.ErrNotImplemented
} else if strings.HasPrefix(img.Path, "sheepdog") {
// sheepdog -> sheepdog[+tcp|+unix]://[host:port]/vdiname[?socket=path][#snapid|#tag]
return cloudprovider.ErrNotImplemented
} else if strings.HasPrefix(img.Path, models.STORAGE_RBD) {
img.ActualSizeBytes = 0
} else {
fileInfo, err := os.Stat(img.Path)
if err != nil {
if !os.IsNotExist(err) {
return err
} else {
return nil
}
} else {
img.ActualSizeBytes = fileInfo.Size()
}
}
cmd := exec.Command(qemutils.GetQemuImg(), "info", img.Path)
if len(img.Password) > 0 {
cmd.Stdin = bytes.NewBuffer([]byte(img.Password))
}
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Errorf("qemu-img info %s fail %s", img.Path, err)
return fmt.Errorf("qemu-img info error %s", err)
}
for {
line, err := out.ReadString('\n')
if len(line) > 0 {
line = strings.TrimSpace(line)
switch {
case strings.HasPrefix(line, "file format:"):
img.Format = TImageFormat(line[strings.LastIndexByte(line, ' ')+1:])
case strings.HasPrefix(line, "virtual size:"):
sizeStr := line[strings.LastIndexByte(line, '(')+1 : strings.LastIndexByte(line, ' ')]
size, err := strconv.ParseInt(sizeStr, 10, -1)
if err != nil {
return fmt.Errorf("invalid size str %s", sizeStr)
}
img.SizeBytes = size
case strings.HasPrefix(line, "cluster_size:"):
sizeStr := line[strings.LastIndexByte(line, ' ')+1:]
size, err := strconv.ParseInt(sizeStr, 10, -1)
if err != nil {
return fmt.Errorf("invalid cluster size str %s", sizeStr)
}
img.ClusterSize = int(size)
case strings.HasPrefix(line, "backing file:"):
img.BackFilePath = line[strings.LastIndexByte(line, ' ')+1:]
case strings.HasPrefix(line, "compat:"):
img.Compat = line[strings.LastIndexByte(line, ' ')+1:]
case strings.HasPrefix(line, "encrypted:"):
if line[strings.LastIndexByte(line, ' ')+1:] == "yes" {
img.Encryption = true
}
case strings.HasPrefix(line, "create type:"):
img.Subformat = line[strings.LastIndexByte(line, ' ')+1:]
}
}
if err != nil {
if err == io.EOF {
break
} else {
log.Errorf("read output fail %s", err)
return fmt.Errorf("read output fail %s", err)
}
}
}
if img.Format == RAW {
// test if it is an ISO
blkType := fileutils2.GetBlkidType(img.Path)
if blkType == "iso9660" {
img.Format = ISO
}
}
return nil
}
func (img *SQemuImage) IsValid() bool {
return len(img.Format) > 0
}
func (img *SQemuImage) IsChained() bool {
return len(img.BackFilePath) > 0
}
func (img *SQemuImage) doConvert(name string, format TImageFormat, options []string, compact bool, password string) error {
if !img.IsValid() {
return fmt.Errorf("self is not valid")
}
cmdline := []string{"convert"}
if compact {
cmdline = append(cmdline, "-c")
}
cmdline = append(cmdline, "-f", img.Format.String(), "-O", format.String())
if len(password) > 0 {
if options == nil {
options = make([]string, 0)
}
options = append(options, "encryption=on")
}
if len(options) > 0 {
cmdline = append(cmdline, "-o", strings.Join(options, ","))
}
cmdline = append(cmdline, img.Path, name)
cmd := exec.Command(qemutils.GetQemuImg(), cmdline...)
if len(img.Password) > 0 || len(password) > 0 {
input := ""
if len(img.Password) > 0 {
input = fmt.Sprintf("%s\r", input, img.Password)
}
if len(password) > 0 {
input = fmt.Sprintf("%s%s\r", input, password)
}
cmd.Stdin = bytes.NewBuffer([]byte(input))
}
err := cmd.Run()
if err != nil {
log.Errorf("clone fail %s", err)
os.Remove(name)
return err
}
return nil
}
func (img *SQemuImage) Clone(name string, format TImageFormat, compact bool) (*SQemuImage, error) {
switch format {
case QCOW2:
return img.CloneQcow2(name, compact)
case VMDK:
return img.CloneVmdk(name, compact)
case RAW:
return img.CloneRaw(name)
default:
return nil, ErrUnsupportedFormat
}
}
func (img *SQemuImage) clone(name string, format TImageFormat, options []string, compact bool, password string) (*SQemuImage, error) {
err := img.doConvert(name, format, options, compact, password)
if err != nil {
return nil, err
}
return NewQemuImage(name)
}
func (img *SQemuImage) convert(format TImageFormat, options []string, compact bool, password string) (error) {
tmpPath := fmt.Sprintf("%s.%s", img.Path, utils.GenRequestId(36))
err := img.doConvert(tmpPath, format, options, compact, password)
if err != nil {
return err
}
cmd := exec.Command("mv", "-f", tmpPath, img.Path)
err = cmd.Run()
if err != nil {
log.Errorf("convert move temp file error %s", err)
os.Remove(tmpPath)
return err
}
img.Password = password
return img.parse()
}
func (img *SQemuImage) Copy(name string) (*SQemuImage, error) {
if !img.IsValid() {
return nil, fmt.Errorf("self is not valid")
}
cmd := exec.Command("cp", "--sparse=always", img.Path, name)
err := cmd.Run()
if err != nil {
log.Errorf("copy fail %s", err)
os.Remove(name)
return nil, err
}
return NewQemuImage(name)
}
func (img *SQemuImage) Convert2Qcow2(compact bool) error {
options := make([]string, 0)
// if len(backPath) > 0 {
// options = append(options, fmt.Sprintf("backing_file=%s", backPath))
//} else
if !compact {
sparseOpts := qcow2SparseOptions()
options = append(options, sparseOpts...)
}
return img.convert(QCOW2, options, compact, "")
}
func (img *SQemuImage) Convert2Vmdk(compact bool) error {
return img.convert(VMDK, vmdkOptions(compact), false, "")
}
func (img *SQemuImage) Convert2Raw() error {
return img.convert(RAW, nil, false, "")
}
func (img *SQemuImage) IsRaw() bool {
return img.Format == RAW
}
func (img *SQemuImage) IsSparseQcow2() bool {
return img.Format == QCOW2 && img.ClusterSize >= 1024*1024*2
}
func (img *SQemuImage) IsSparseVmdk() bool {
return img.Format == VMDK && img.Subformat != "streamOptimized"
}
func (img *SQemuImage) IsSparse() bool {
return img.IsRaw() || img.IsSparseQcow2() || img.IsSparseVmdk()
}
func (img *SQemuImage) Expand() error {
if img.IsSparse() {
return nil
}
return img.Convert2Qcow2(false)
}
func (img *SQemuImage) CloneQcow2(name string, compact bool) (*SQemuImage, error) {
options := make([]string, 0)
//if len(backPath) > 0 {
// options = append(options, fmt.Sprintf("backing_file=%s", backPath))
//} else
if !compact {
sparseOpts := qcow2SparseOptions()
options = append(options, sparseOpts...)
}
return img.clone(name, QCOW2, options, compact, "")
}
func vmdkOptions(compact bool) []string {
if compact {
return []string{"subformat=streamOptimized"}
} else {
return []string{"subformat=monolithicSparse"}
}
}
func (img *SQemuImage) CloneVmdk(name string, compact bool) (*SQemuImage, error) {
return img.clone(name, VMDK, vmdkOptions(compact), compact, "")
}
func (img *SQemuImage) CloneRaw(name string) (*SQemuImage, error) {
return img.clone(name, RAW, nil, false, "")
}
func (img *SQemuImage) create(sizeMB int, format TImageFormat, options []string) error {
if img.IsValid() {
return fmt.Errorf("The image is valid???")
}
args := []string{"create", "-f", format.String()}
if len(options) > 0 {
args = append(args, "-o", strings.Join(options, ","))
}
args = append(args, img.Path)
if sizeMB > 0 {
args = append(args, fmt.Sprintf("%dM", sizeMB))
}
cmd := exec.Command(qemutils.GetQemuImg(), args...)
err := cmd.Run()
if err != nil {
log.Errorf("create error %s", err)
return err
}
return img.parse()
}
func (img *SQemuImage) CreateQcow2(sizeMB int, compact bool, backPath string) error {
options := make([]string, 0)
if len(backPath) > 0 {
options = append(options, fmt.Sprintf("backing_file=%s", backPath))
if !compact {
options = append(options, "cluster_size=2M")
}
} else if !compact {
sparseOpts := qcow2SparseOptions()
options = append(options, sparseOpts...)
}
return img.create(sizeMB, QCOW2, options)
}
func (img *SQemuImage) CreateVmdk(sizeMB int, compact bool) error {
return img.create(sizeMB, VMDK, vmdkOptions(compact))
}
func (img *SQemuImage) CreateRaw(sizeMB int) error {
return img.create(sizeMB, RAW, nil)
}
func (img *SQemuImage) GetSizeMB() int {
return int(img.SizeBytes / 1024 / 1024)
}
func (img *SQemuImage) GetActualSizeMB() int {
return int(img.ActualSizeBytes / 1024 / 1024)
}
func (img *SQemuImage) Resize(sizeMB int) error {
if !img.IsValid() {
return fmt.Errorf("self is not valid")
}
cmd := exec.Command(qemutils.GetQemuImg(), "resize", img.Path, fmt.Sprintf("%dM", sizeMB))
err := cmd.Run()
if err != nil {
log.Errorf("resize fail %s", err)
return err
}
return img.parse()
}
func (img *SQemuImage) Rebase(backPath string, force bool) error {
if !img.IsValid() {
return fmt.Errorf("self is not valid")
}
args := []string{"rebase"}
if force {
args = append(args, "-u")
}
args = append(args, "-b", backPath, img.Path)
cmd := exec.Command(qemutils.GetQemuImg(), args...)
err := cmd.Run()
if err != nil {
log.Errorf("rebase fail %s", err)
return err
}
return img.parse()
}
func (img *SQemuImage) Delete() error {
if !img.IsValid() {
return nil
}
err := os.Remove(img.Path)
if err != nil {
log.Errorf("delete fail %s", err)
return err
}
img.Format = ""
img.ActualSizeBytes = 0
img.SizeBytes = 0
return nil
}
func (img *SQemuImage) Fallocate() error {
if !img.IsValid() {
return fmt.Errorf("self is not valid")
}
cmd := exec.Command("fallocate", "-l", fmt.Sprintf("%dm", img.GetSizeMB()), img.Path)
return cmd.Run()
}
func (img *SQemuImage) String() string {
return fmt.Sprintf("Qemu %s %d(%d) %s", img.Format, img.GetSizeMB(), img.GetActualSizeMB(), img.Path)
}

View File

@@ -0,0 +1,150 @@
package qemuimg
import "testing"
func TestGetQemuImgVersion(t *testing.T) {
verStr := `qemu-img version 1.5.3, Copyright (c) 2004-2008 Fabrice Bellard`
matches := qemuImgVersionRegexp.FindStringSubmatch(verStr)
t.Logf("%s", matches[1])
}
func TestQcow2(t *testing.T) {
img, err := NewQemuImage("test")
if err != nil {
t.Errorf(err.Error())
return
}
err = img.CreateQcow2(1000, true, "")
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Delete()
if err != nil {
t.Errorf(err.Error())
return
}
err = img.CreateQcow2(1000, false, "")
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Convert2Qcow2(true)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Convert2Qcow2(false)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Resize(2048)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Convert2Qcow2(true)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Expand()
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Delete()
if err != nil {
t.Errorf(err.Error())
return
}
err = img.CreateQcow2(1000, true, "")
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
img2, err := img.CloneQcow2("test2", true)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img2, img2.IsSparse())
err = img2.Delete()
if err != nil {
t.Errorf(err.Error())
return
}
err = img.Convert2Qcow2(false)
if err != nil {
t.Errorf(err.Error())
return
}
img4, err := NewQemuImage("test_top")
if err != nil {
t.Errorf(err.Error())
return
}
err = img4.CreateQcow2(0, true, img.Path)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
t.Logf("%s %v", img4, img4.IsSparse())
err = img.Convert2Qcow2(true)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
t.Logf("%s %v", img4, img4.IsSparse())
img4.Delete()
img.Delete()
}
func TestVmdk(t *testing.T) {
img, err := NewQemuImage("test")
if err != nil {
t.Errorf(err.Error())
return
}
err = img.CreateVmdk(1024, true)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Delete()
if err != nil {
t.Errorf(err.Error())
return
}
err = img.CreateVmdk(1024, false)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Convert2Vmdk(true)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
err = img.Convert2Vmdk(false)
if err != nil {
t.Errorf(err.Error())
return
}
t.Logf("%s %v", img, img.IsSparse())
img.Delete()
}

View File

@@ -0,0 +1,112 @@
package qemutils
import (
"fmt"
"io/ioutil"
"os"
"path"
"regexp"
"sort"
"strings"
"yunion.io/x/onecloud/pkg/cloudcommon/version"
)
const (
USER_LOCAL_BIN = "/usr/local/bin"
USER_BIN = "/usr/bin"
)
func GetQemu(version string) string {
return getQemuCmd("qemu-system-x86_64", version)
}
func GetQemuNbd() string {
return getQemuCmd("qemu-nbd", "")
}
func GetQemuImg() string {
return getQemuCmd("qemu-img", "")
}
func getQemuCmd(cmd, version string) string {
if len(version) > 0 {
return getQemuCmdByVersion(cmd, version)
} else {
return getQemuDefaultCmd(cmd)
}
}
func getQemuCmdByVersion(cmd, version string) string {
p := path.Join(fmt.Sprintf("/usr/local/qemu-%s/bin", version), cmd)
if _, err := os.Stat(p); !os.IsNotExist(err) {
return p
}
cmd = cmd + "_" + version
p = path.Join(USER_LOCAL_BIN, cmd)
if _, err := os.Stat(p); !os.IsNotExist(err) {
return p
}
p = path.Join(USER_BIN, cmd)
if _, err := os.Stat(p); !os.IsNotExist(err) {
return p
}
return ""
}
func getQemuVersion(verString string) string {
s := regexp.MustCompile(`qemu-(?P<ver>\d+(\.\d+)+)$`).FindString(verString)
if len(s) == 0 {
return ""
}
return s[len("qemu-"):]
}
func getCmdVersion(cmd string) string {
s := regexp.MustCompile(`_(?P<ver>\d+(\.\d+)+)$`).FindString(cmd)
if len(s) == 0 {
return ""
}
return s[1:]
}
func getQemuDefaultCmd(cmd string) string {
var qemus = make([]string, 0)
if files, err := ioutil.ReadDir("/usr/local"); err == nil {
for i := 0; i < len(files); i++ {
if strings.HasPrefix(files[i].Name(), "qemu-") {
qemus = append(qemus, files[i].Name())
}
}
if len(qemus) > 0 {
sort.Slice(qemus, func(i, j int) bool {
return version.LT(getQemuVersion(qemus[i]),
getQemuVersion(qemus[j]))
})
p := fmt.Sprintf("/usr/local/%s/bin/%s", qemus[len(qemus)-1], cmd)
if _, err := os.Stat(p); !os.IsNotExist(err) {
return p
}
}
}
cmds := make([]string, 0)
if files, err := ioutil.ReadDir(USER_LOCAL_BIN); err == nil {
for i := 0; i < len(files); i++ {
if strings.HasPrefix(files[i].Name(), cmd) {
cmds = append(cmds, files[i].Name())
}
}
if len(cmds) > 0 {
sort.Slice(cmds, func(i, j int) bool {
return version.LT(getCmdVersion(cmds[i]),
getCmdVersion(cmds[j]))
})
p := path.Join(USER_LOCAL_BIN, cmds[len(cmds)-1])
if _, err := os.Stat(p); !os.IsNotExist(err) {
return p
}
}
}
return ""
}

View File

@@ -0,0 +1,47 @@
package version
import (
"strconv"
"strings"
)
func less(v1Str, v2Str string) (bool, bool) {
v1 := strings.Split(v1Str, ".")
v2 := strings.Split(v2Str, ".")
var i = 0
for ; i < len(v2); i++ {
if i >= len(v1) {
return true, false
}
v, _ := strconv.ParseInt(v2[i], 10, 0)
compareV, _ := strconv.ParseInt(v1[i], 10, 0)
if v < compareV {
return false, false
} else if compareV < v {
return true, false
}
}
if i < len(v1)-1 {
return false, false
}
return true, true
}
func LE(v1Str, v2Str string) bool {
l, _ := less(v1Str, v2Str)
return l
}
func LT(v1Str, v2Str string) bool {
l, e := less(v1Str, v2Str)
return l && !e
}
func GT(v1Str, v2Str string) bool {
return LT(v2Str, v1Str)
}
func GE(v1Str, v2Str string) bool {
return LE(v2Str, v1Str)
}

View File

@@ -5,12 +5,12 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/etcd"
)
type CloudirOptions struct {
type SCloudirOptions struct {
etcd.SEtcdOptions
cloudcommon.Options
}
var (
Options CloudirOptions
Options SCloudirOptions
)

View File

@@ -1,4 +1,5 @@
package images
func AddHandler() {
func AddHandler()
}

View File

@@ -169,11 +169,14 @@ func (self *SBaremetalagent) GetZone() *SZone {
return nil
}
func (self *SBaremetalagent) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
func (self *SBaremetalagent) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
zone := self.GetZone()
if zone != nil {
extra.Set("zone", jsonutils.NewString(zone.GetName()))
}
return extra
return extra, nil
}

View File

@@ -644,9 +644,12 @@ func (self *SCloudaccount) GetCustomizeColumns(ctx context.Context, userCred mcc
return self.getMoreDetails(extra)
}
func (self *SCloudaccount) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(extra)
func (self *SCloudaccount) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(extra), nil
}
func migrateCloudprovider(cloudprovider *SCloudprovider) error {

View File

@@ -589,9 +589,12 @@ func (self *SCloudprovider) GetCustomizeColumns(ctx context.Context, userCred mc
return self.getMoreDetails(ctx, extra)
}
func (self *SCloudprovider) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(ctx, extra)
func (self *SCloudprovider) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(ctx, extra), nil
}
func (manager *SCloudproviderManager) InitializeData() error {

View File

@@ -140,9 +140,12 @@ func (self *SCloudregion) GetCustomizeColumns(ctx context.Context, userCred mccl
return self.getMoreDetails(extra)
}
func (self *SCloudregion) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(extra)
func (self *SCloudregion) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(extra), nil
}
func (manager *SCloudregionManager) GetRegionByProvider(provider string) ([]SCloudregion, error) {

View File

@@ -372,7 +372,7 @@ func (manager *SDiskManager) ValidateCreateData(ctx context.Context, userCred mc
}
pendingUsage := SQuota{Storage: diskConfig.SizeMb}
if err := QuotaManager.CheckSetPendingQuota(ctx, userCred, userCred.GetProjectId(), &pendingUsage); err != nil {
return nil, err
return nil, httperrors.NewOutOfQuotaError("%s", err)
}
return data, nil
}
@@ -719,10 +719,13 @@ func (self *SDisk) PrepareSaveImage(ctx context.Context, userCred mcclient.Token
} else if imageList.Total > 0 {
return "", httperrors.NewConflictError("Duplicate image name %s", name)
}
quota := SQuota{Image: 1}
if _, err := QuotaManager.CheckQuota(ctx, userCred, userCred.GetProjectId(), &quota); err != nil {
/*
no need to check quota anymore
session := auth.GetSession(userCred, options.Options.Region, "v2")
quota := image_models.SQuota{Image: 1}
if _, err := modules.ImageQuotas.DoQuotaCheck(session, jsonutils.Marshal(&quota)); err != nil {
return "", err
}
}*/
data.Add(jsonutils.NewInt(int64(self.DiskSize)), "virtual_size")
if result, err := modules.Images.Create(s, data); err != nil {
return "", err
@@ -1354,9 +1357,12 @@ func (self *SDisk) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict
return extra
}
func (self *SDisk) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(extra)
func (self *SDisk) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(extra), nil
}
func (self *SDisk) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {

View File

@@ -131,9 +131,12 @@ func (self *SDynamicschedtag) GetCustomizeColumns(ctx context.Context, userCred
return self.getMoreColumns(extra)
}
func (self *SDynamicschedtag) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreColumns(extra)
func (self *SDynamicschedtag) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreColumns(extra), nil
}
func (manager *SDynamicschedtagManager) getAllEnabledDynamicSchedtags() []SDynamicschedtag {

View File

@@ -704,9 +704,12 @@ func (self *SElasticip) StartEipSyncstatusTask(ctx context.Context, userCred mcc
return nil
}
func (self *SElasticip) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(extra)
func (self *SElasticip) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(extra), nil
}
func (self *SElasticip) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {

View File

@@ -49,9 +49,12 @@ func (self *SGroupguest) GetCustomizeColumns(ctx context.Context, userCred mccli
return db.JointModelExtra(self, extra)
}
func (self *SGroupguest) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SGroupJointsBase.GetExtraDetails(ctx, userCred, query)
return db.JointModelExtra(self, extra)
func (self *SGroupguest) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SGroupJointsBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return db.JointModelExtra(self, extra), nil
}
func (self *SGroupguest) GetGuest() *SGuest {

View File

@@ -48,9 +48,12 @@ func (self *SGroupnetwork) GetCustomizeColumns(ctx context.Context, userCred mcc
return db.JointModelExtra(self, extra)
}
func (self *SGroupnetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SGroupJointsBase.GetExtraDetails(ctx, userCred, query)
return db.JointModelExtra(self, extra)
func (self *SGroupnetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SGroupJointsBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return db.JointModelExtra(self, extra), nil
}
func (self *SGroupnetwork) getNetwork() *SNetwork {

View File

@@ -1069,7 +1069,7 @@ func (self *SGuest) PerformCreatedisk(ctx context.Context, userCred mcclient.Tok
err := QuotaManager.CheckSetPendingQuota(ctx, userCred, self.ProjectId, pendingUsage)
if err != nil {
logclient.AddActionLog(self, logclient.ACT_CREATE, err.Error(), userCred, false)
return nil, httperrors.NewBadRequestError(err.Error())
return nil, httperrors.NewOutOfQuotaError(err.Error())
}
lockman.LockObject(ctx, host)
@@ -1500,7 +1500,7 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
if !pendingUsage.IsEmpty() {
err := QuotaManager.CheckSetPendingQuota(ctx, userCred, userCred.GetProjectId(), pendingUsage)
if err != nil {
return nil, httperrors.NewBadRequestError("Check set pending quota error %s", err)
return nil, httperrors.NewOutOfQuotaError("Check set pending quota error %s", err)
}
}
if newDisks.Length() > 0 {
@@ -1642,7 +1642,7 @@ func (self *SGuest) PerformDiskSnapshot(ctx context.Context, userCred mcclient.T
pendingUsage := &SQuota{Snapshot: 1}
err = QuotaManager.CheckSetPendingQuota(ctx, userCred, self.ProjectId, pendingUsage)
if err != nil {
return nil, httperrors.NewBadRequestError("Check set pending quota error %s", err)
return nil, httperrors.NewOutOfQuotaError("Check set pending quota error %s", err)
}
snapshot, err := SnapshotManager.CreateSnapshot(ctx, userCred, MANUAL, diskId, self.Id, "", name)
QuotaManager.CancelPendingUsage(ctx, userCred, self.ProjectId, nil, pendingUsage)
@@ -2078,7 +2078,7 @@ func (self *SGuest) PerformCreateBackup(ctx context.Context, userCred mcclient.T
req := self.getGuestBackupResourceRequirements(ctx, userCred)
err := QuotaManager.CheckSetPendingQuota(ctx, userCred, self.GetOwnerProjectId(), &req)
if err != nil {
return nil, err
return nil, httperrors.NewOutOfQuotaError(err.Error())
}
params := data.(*jsonutils.JSONDict)

View File

@@ -96,10 +96,13 @@ func (self *SGuestdisk) GetCustomizeColumns(ctx context.Context, userCred mcclie
return self.getExtraInfo(extra)
}
func (self *SGuestdisk) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SGuestJointsBase.GetExtraDetails(ctx, userCred, query)
func (self *SGuestdisk) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SGuestJointsBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra = db.JointModelExtra(self, extra)
return self.getExtraInfo(extra)
return self.getExtraInfo(extra), nil
}
func (self *SGuestdisk) DoSave(driver string, cache string, mountpoint string) error {

View File

@@ -73,9 +73,12 @@ func (self *SGuestnetwork) GetCustomizeColumns(ctx context.Context, userCred mcc
return db.JointModelExtra(self, extra)
}
func (self *SGuestnetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SGuestJointsBase.GetExtraDetails(ctx, userCred, query)
return db.JointModelExtra(self, extra)
func (self *SGuestnetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SGuestJointsBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return db.JointModelExtra(self, extra), nil
}
func (manager *SGuestnetworkManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {

View File

@@ -1208,8 +1208,12 @@ func (self *SGuest) moreExtraInfo(extra *jsonutils.JSONDict) *jsonutils.JSONDict
return extra
}
func (self *SGuest) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
func (self *SGuest) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra.Add(jsonutils.NewString(self.getNetworksDetails()), "networks")
extra.Add(jsonutils.NewString(self.getDisksDetails()), "disks")
extra.Add(self.getDisksInfoDetails(), "disks_info")
@@ -1261,7 +1265,7 @@ func (self *SGuest) GetExtraDetails(ctx context.Context, userCred mcclient.Token
extra.Add(jsonutils.JSONFalse, "is_prepaid_recycle")
}
return self.moreExtraInfo(extra)
return self.moreExtraInfo(extra), nil
}
func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {

View File

@@ -57,9 +57,12 @@ func (bn *SHostnetwork) GetCustomizeColumns(ctx context.Context, userCred mcclie
return extra
}
func (bn *SHostnetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := bn.SHostJointsBase.GetExtraDetails(ctx, userCred, query)
return db.JointModelExtra(bn, extra)
func (bn *SHostnetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := bn.SHostJointsBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return db.JointModelExtra(bn, extra), nil
}
func (bn *SHostnetwork) GetHost() *SHost {

View File

@@ -2125,9 +2125,12 @@ func (self *SHost) GetCustomizeColumns(ctx context.Context, userCred mcclient.To
return self.getMoreDetails(ctx, extra)
}
func (self *SHost) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(ctx, extra)
func (self *SHost) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(ctx, extra), nil
}
func (self *SHost) AllowGetDetailsVnc(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
@@ -2195,8 +2198,8 @@ func (manager *SHostManager) GetHostsByManagerAndRegion(managerId string, region
return nil
}*/
func (self *SHost) Request(userCred mcclient.TokenCredential, method string, url string, headers http.Header, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
s := auth.GetSession(nil, userCred, "", "")
func (self *SHost) Request(ctx context.Context, userCred mcclient.TokenCredential, method httputils.THttpMethod, url string, headers http.Header, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
s := auth.GetSession(ctx, userCred, "", "")
_, ret, err := s.JSONRequest(self.ManagerUri, "", method, url, headers, body)
return ret, err
}
@@ -2605,7 +2608,7 @@ func (self *SHost) StartBaremetalUnmaintenanceTask(ctx context.Context, userCred
return nil
}
func (self *SHost) BaremetalSyncRequest(ctx context.Context, method, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
func (self *SHost) BaremetalSyncRequest(ctx context.Context, method httputils.THttpMethod, url string, headers http.Header, body *jsonutils.JSONDict) (jsonutils.JSONObject, error) {
serviceUrl, err := auth.GetServiceURL("baremetal", options.Options.Region, self.GetZone().GetName(), "")
if err != nil {
return nil, err

View File

@@ -49,9 +49,12 @@ func (self *SHostschedtag) GetCustomizeColumns(ctx context.Context, userCred mcc
return db.JointModelExtra(self, extra)
}
func (self *SHostschedtag) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SHostJointsBase.GetExtraDetails(ctx, userCred, query)
return db.JointModelExtra(self, extra)
func (self *SHostschedtag) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SHostJointsBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return db.JointModelExtra(self, extra), nil
}
func (self *SHostschedtag) getHost() *SHost {

View File

@@ -63,10 +63,13 @@ func (self *SHoststorage) GetCustomizeColumns(ctx context.Context, userCred mccl
return self.getExtraDetails(extra)
}
func (self *SHoststorage) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SHostJointsBase.GetExtraDetails(ctx, userCred, query)
func (self *SHoststorage) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SHostJointsBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra = db.JointModelExtra(self, extra)
return self.getExtraDetails(extra)
return self.getExtraDetails(extra), nil
}
func (self *SHoststorage) GetHost() *SHost {

View File

@@ -56,10 +56,13 @@ func (self *SHostwire) GetCustomizeColumns(ctx context.Context, userCred mcclien
return self.getExtraDetails(extra)
}
func (self *SHostwire) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SHostJointsBase.GetExtraDetails(ctx, userCred, query)
func (self *SHostwire) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SHostJointsBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra = db.JointModelExtra(self, extra)
return self.getExtraDetails(extra)
return self.getExtraDetails(extra), nil
}
func (hw *SHostwire) GetWire() *SWire {

View File

@@ -498,10 +498,13 @@ func (self *SIsolatedDevice) getMoreDetails(extra *jsonutils.JSONDict) *jsonutil
return extra
}
func (self *SIsolatedDevice) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
func (self *SIsolatedDevice) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra = self.getMoreDetails(extra)
return extra
return extra, nil
}
func (self *SIsolatedDevice) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {

View File

@@ -81,8 +81,11 @@ func (self *SKeypair) GetCustomizeColumns(ctx context.Context, userCred mcclient
return extra
}
func (self *SKeypair) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
func (self *SKeypair) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra.Add(jsonutils.NewInt(int64(len(self.PrivateKey))), "private_key_len")
extra.Add(jsonutils.NewInt(int64(self.GetLinkedGuestsCount())), "linked_guest_count")
if db.IsAdminAllowGet(userCred, self) {
@@ -92,7 +95,7 @@ func (self *SKeypair) GetExtraDetails(ctx context.Context, userCred mcclient.Tok
extra.Add(jsonutils.NewString(uc.Name), "owner_name")
}
}
return extra
return extra, nil
}
func (manager *SKeypairManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {

View File

@@ -118,9 +118,9 @@ func (lbbg *SLoadbalancerBackendGroup) GetCustomizeColumns(ctx context.Context,
return extra
}
func (lbbg *SLoadbalancerBackendGroup) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
func (lbbg *SLoadbalancerBackendGroup) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra := lbbg.GetCustomizeColumns(ctx, userCred, query)
return extra
return extra, nil
}
func (lbbg *SLoadbalancerBackendGroup) PreDelete(ctx context.Context, userCred mcclient.TokenCredential) {

View File

@@ -171,9 +171,9 @@ func (lbr *SLoadbalancerListenerRule) GetCustomizeColumns(ctx context.Context, u
return extra
}
func (lbr *SLoadbalancerListenerRule) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
func (lbr *SLoadbalancerListenerRule) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra := lbr.GetCustomizeColumns(ctx, userCred, query)
return extra
return extra, nil
}
func (lbr *SLoadbalancerListenerRule) PreDelete(ctx context.Context, userCred mcclient.TokenCredential) {

View File

@@ -389,9 +389,9 @@ func (lblis *SLoadbalancerListener) GetCustomizeColumns(ctx context.Context, use
return extra
}
func (lblis *SLoadbalancerListener) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
func (lblis *SLoadbalancerListener) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra := lblis.GetCustomizeColumns(ctx, userCred, query)
return extra
return extra, nil
}
func (lblis *SLoadbalancerListener) PreDelete(ctx context.Context, userCred mcclient.TokenCredential) {

View File

@@ -194,9 +194,9 @@ func (lb *SLoadbalancer) GetCustomizeColumns(ctx context.Context, userCred mccli
return extra
}
func (lb *SLoadbalancer) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
func (lb *SLoadbalancer) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra := lb.GetCustomizeColumns(ctx, userCred, query)
return extra
return extra, nil
}
func (lb *SLoadbalancer) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {

View File

@@ -869,10 +869,13 @@ func (self *SNetwork) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSOND
return extra
}
func (self *SNetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
func (self *SNetwork) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra = self.getMoreDetails(extra)
return extra
return extra, nil
}
func (self *SNetwork) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {

View File

@@ -8,8 +8,6 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
"yunion.io/x/onecloud/pkg/compute/options"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/pkg/tristate"
)
@@ -23,16 +21,16 @@ func init() {
}
var (
ErrOutOfCPU = errors.New("out of CPU quota")
ErrOutOfMemory = errors.New("out of memory quota")
ErrOutOfStorage = errors.New("out of storage quota")
ErrOutOfPort = errors.New("out of internal port quota")
ErrOutOfEip = errors.New("out of eip quota")
ErrOutOfEport = errors.New("out of external port quota")
ErrOutOfBw = errors.New("out of internal bandwidth quota")
ErrOutOfEbw = errors.New("out of external bandwidth quota")
ErrOutOfKeypair = errors.New("out of keypair quota")
ErrOutOfImage = errors.New("out of image quota")
ErrOutOfCPU = errors.New("out of CPU quota")
ErrOutOfMemory = errors.New("out of memory quota")
ErrOutOfStorage = errors.New("out of storage quota")
ErrOutOfPort = errors.New("out of internal port quota")
ErrOutOfEip = errors.New("out of eip quota")
ErrOutOfEport = errors.New("out of external port quota")
ErrOutOfBw = errors.New("out of internal bandwidth quota")
ErrOutOfEbw = errors.New("out of external bandwidth quota")
ErrOutOfKeypair = errors.New("out of keypair quota")
// ErrOutOfImage = errors.New("out of image quota")
ErrOutOfGroup = errors.New("out of group quota")
ErrOutOfSecgroup = errors.New("out of secgroup quota")
ErrOutOfIsolatedDevice = errors.New("out of isolated device quota")
@@ -40,16 +38,16 @@ var (
)
type SQuota struct {
Cpu int
Memory int
Storage int
Port int
Eip int
Eport int
Bw int
Ebw int
Keypair int
Image int
Cpu int
Memory int
Storage int
Port int
Eip int
Eport int
Bw int
Ebw int
Keypair int
// Image int
Group int
Secgroup int
IsolatedDevice int
@@ -66,7 +64,6 @@ func (self *SQuota) FetchSystemQuota() {
self.Bw = options.Options.DefaultBwQuota
self.Ebw = options.Options.DefaultEbwQuota
self.Keypair = options.Options.DefaultKeypairQuota
self.Image = options.Options.DefaultImageQuota
self.Group = options.Options.DefaultGroupQuota
self.Secgroup = options.Options.DefaultSecgroupQuota
self.IsolatedDevice = options.Options.DefaultIsolatedDeviceQuota
@@ -92,8 +89,6 @@ func (self *SQuota) FetchUsage(ctx context.Context, projectId string) error {
self.Bw = net.InternalBandwidth
self.Ebw = net.ExternalBandwidth
self.Keypair = 0 // keypair
s := auth.GetAdminSession(ctx, options.Options.Region, "")
self.Image, _ = modules.Images.GetPrivateImageCount(s, projectId, true)
self.Group = 0
self.Secgroup = totalSecurityGroupCount(projectId)
self.IsolatedDevice = guest.TotalIsolatedCount
@@ -129,9 +124,6 @@ func (self *SQuota) IsEmpty() bool {
if self.Keypair > 0 {
return false
}
if self.Image > 0 {
return false
}
if self.Group > 0 {
return false
}
@@ -158,7 +150,6 @@ func (self *SQuota) Add(quota quotas.IQuota) {
self.Bw = self.Bw + squota.Bw
self.Ebw = self.Ebw + squota.Ebw
self.Keypair = self.Keypair + squota.Keypair
self.Image = self.Image + squota.Image
self.Group = self.Group + squota.Group
self.Secgroup = self.Secgroup + squota.Secgroup
self.IsolatedDevice = self.IsolatedDevice + squota.IsolatedDevice
@@ -166,11 +157,7 @@ func (self *SQuota) Add(quota quotas.IQuota) {
}
func nonNegative(val int) int {
if val < 0 {
return 0
} else {
return val
}
return quotas.NonNegative(val)
}
func (self *SQuota) Sub(quota quotas.IQuota) {
@@ -184,7 +171,6 @@ func (self *SQuota) Sub(quota quotas.IQuota) {
self.Bw = nonNegative(self.Bw - squota.Bw)
self.Ebw = nonNegative(self.Ebw - squota.Ebw)
self.Keypair = nonNegative(self.Keypair - squota.Keypair)
self.Image = nonNegative(self.Image - squota.Image)
self.Group = nonNegative(self.Group - squota.Group)
self.Secgroup = nonNegative(self.Secgroup - squota.Secgroup)
self.IsolatedDevice = nonNegative(self.IsolatedDevice - squota.IsolatedDevice)
@@ -220,9 +206,6 @@ func (self *SQuota) Update(quota quotas.IQuota) {
if squota.Keypair > 0 {
self.Keypair = squota.Keypair
}
if squota.Image > 0 {
self.Image = squota.Image
}
if squota.Group > 0 {
self.Group = squota.Group
}
@@ -267,9 +250,6 @@ func (self *SQuota) Exceed(request quotas.IQuota, quota quotas.IQuota) error {
if sreq.Keypair > 0 && self.Keypair > squota.Keypair {
return ErrOutOfKeypair
}
if sreq.Image > 0 && self.Image > squota.Image {
return ErrOutOfImage
}
if sreq.Group > 0 && self.Group > squota.Group {
return ErrOutOfGroup
}
@@ -322,9 +302,6 @@ func (self *SQuota) ToJSON(prefix string) jsonutils.JSONObject {
if self.Keypair > 0 {
ret.Add(jsonutils.NewInt(int64(self.Keypair)), keyName(prefix, "keypair"))
}
if self.Image > 0 {
ret.Add(jsonutils.NewInt(int64(self.Image)), keyName(prefix, "image"))
}
if self.Group > 0 {
ret.Add(jsonutils.NewInt(int64(self.Group)), keyName(prefix, "group"))
}

View File

@@ -327,10 +327,10 @@ func (rt *SRouteTable) GetCustomizeColumns(ctx context.Context, userCred mcclien
return extra
}
func (rt *SRouteTable) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
func (rt *SRouteTable) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra := rt.GetCustomizeColumns(ctx, userCred, query)
extra = rt.getMoreDetails(extra)
return extra
return extra, nil
}
func (man *SRouteTableManager) SyncRouteTables(ctx context.Context, userCred mcclient.TokenCredential, vpc *SVpc, cloudRouteTables []cloudprovider.ICloudRouteTable) ([]SRouteTable, []cloudprovider.ICloudRouteTable, compare.SyncResult) {

View File

@@ -120,9 +120,12 @@ func (self *SSchedpolicy) GetCustomizeColumns(ctx context.Context, userCred mccl
return self.getMoreColumns(extra)
}
func (self *SSchedpolicy) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreColumns(extra)
func (self *SSchedpolicy) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreColumns(extra), nil
}
func (manager *SSchedpolicyManager) getAllEnabledPolicies() []SSchedpolicy {

View File

@@ -193,9 +193,12 @@ func (self *SSchedtag) GetCustomizeColumns(ctx context.Context, userCred mcclien
return self.getMoreColumns(extra)
}
func (self *SSchedtag) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreColumns(extra)
func (self *SSchedtag) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreColumns(extra), nil
}
/*func (self *SSchedtag) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {

View File

@@ -84,15 +84,18 @@ func (self *SSecurityGroup) getDesc() jsonutils.JSONObject {
return desc
}
func (self *SSecurityGroup) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
func (self *SSecurityGroup) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra.Add(jsonutils.NewInt(int64(len(self.GetGuests()))), "guest_cnt")
extra.Add(jsonutils.NewString(self.getSecurityRuleString("")), "rules")
return extra
return extra, nil
}
func (self *SSecurityGroup) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SSharableVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
extra := self.SSharableVirtualResourceBase.GetCustomizeColumns(ctx, userCred, query)
extra.Add(jsonutils.NewInt(int64(len(self.GetGuests()))), "guest_cnt")
extra.Add(jsonutils.NewTimeString(self.CreatedAt), "created_at")
extra.Add(jsonutils.NewString(self.Description), "description")

View File

@@ -162,9 +162,12 @@ func (self *SSnapshot) GetCustomizeColumns(ctx context.Context, userCred mcclien
return self.getMoreDetails(extra)
}
func (self *SSnapshot) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(extra)
func (self *SSnapshot) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(extra), nil
}
func (self *SSnapshot) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict {

View File

@@ -139,10 +139,13 @@ func (self *SStoragecachedimage) GetCustomizeColumns(ctx context.Context, userCr
return self.getExtraDetails(extra)
}
func (self *SStoragecachedimage) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SJointResourceBase.GetExtraDetails(ctx, userCred, query)
func (self *SStoragecachedimage) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SJointResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra = db.JointModelExtra(self, extra)
return self.getExtraDetails(extra)
return self.getExtraDetails(extra), nil
}
func (manager *SStoragecachedimageManager) AllowListDescendent(ctx context.Context, userCred mcclient.TokenCredential, model db.IStandaloneModel, query jsonutils.JSONObject) bool {

View File

@@ -185,10 +185,13 @@ func (self *SStoragecache) syncWithCloudStoragecache(cloudCache cloudprovider.IC
return err
}
func (self *SStoragecache) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
func (self *SStoragecache) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
extra = self.getMoreDetails(extra)
return extra
return extra, nil
}
func (self *SStoragecache) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {

View File

@@ -459,9 +459,12 @@ func (self *SStorage) GetCustomizeColumns(ctx context.Context, userCred mcclient
return self.getMoreDetails(extra)
}
func (self *SStorage) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(extra)
func (self *SStorage) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(extra), nil
}
func (self *SStorage) GetUsedCapacity(isReady tristate.TriState) int {

View File

@@ -193,9 +193,12 @@ func (self *SVpc) GetCustomizeColumns(ctx context.Context, userCred mcclient.Tok
return self.getMoreDetails(extra)
}
func (self *SVpc) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(extra)
func (self *SVpc) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(extra), nil
}
func (manager *SVpcManager) getVpcsByRegion(region *SCloudregion, provider *SCloudprovider) ([]SVpc, error) {

View File

@@ -692,9 +692,12 @@ func (self *SWire) GetCustomizeColumns(ctx context.Context, userCred mcclient.To
return self.getMoreDetails(extra)
}
func (self *SWire) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return self.getMoreDetails(extra)
func (self *SWire) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return self.getMoreDetails(extra), nil
}
func (self *SWire) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict {

View File

@@ -314,9 +314,12 @@ func (zone *SZone) GetCustomizeColumns(ctx context.Context, userCred mcclient.To
return zoneExtra(zone, extra)
}
func (zone *SZone) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := zone.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
return zoneExtra(zone, extra)
func (zone *SZone) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := zone.SStandaloneResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
return zoneExtra(zone, extra), nil
}
func (zone *SZone) GetCloudRegionId() string {

View File

@@ -2,6 +2,7 @@ package options
import (
"yunion.io/x/onecloud/pkg/cloudcommon"
"yunion.io/x/onecloud/pkg/cloudcommon/pending_delete"
)
type ComputeOptions struct {
@@ -21,10 +22,7 @@ type ComputeOptions struct {
DefaultDiskSize int `default:"30720" help:"Default disk size in MB if not specified, default to 30GiB"`
EnablePendingDelete bool `default:"true" help:"Turn on/off pending delete VM and disk, default is on"`
PendingDeleteCheckSeconds int `default:"3600" help:"How long to wait to scan pending delete VM or disks, default is 1 hour"`
PendingDeleteExpireSeconds int `default:"259200" help:"How long a pending delete VM/disks cleaned automatically, default 3 days"`
PendingDeleteMaxCleanBatchSize int `default:"50" help:"How many pending delete servers can be clean in a batch"`
pending_delete.SPendingDeleteOptions
PrepaidExpireCheckSeconds int `default:"600" help:"How long to wait to scan expired prepaid VM or disks, default is 10 minutes"`
ExpiredPrepaidMaxCleanBatchSize int `default:"50" help:"How many expired prepaid servers can be deleted in a batch"`
@@ -45,7 +43,6 @@ type ComputeOptions struct {
DefaultBwQuota int `default:"2000000" help:"Common network port bandwidth in mbps quota per tenant, default 200*10Gbps"`
DefaultEbwQuota int `default:"4000" help:"Common exit network port bandwidth quota per tenant, default 4Gbps"`
DefaultKeypairQuota int `default:"50" help:"Common keypair quota per tenant, default 50"`
DefaultImageQuota int `default:"5" help:"Common image quota per tenant, default 5"`
DefaultGroupQuota int `default:"50" help:"Common group quota per tenant, default 50"`
DefaultSecgroupQuota int `default:"50" help:"Common security group quota per tenant, default 50"`
DefaultIsolatedDeviceQuota int `default:"200" help:"Common isolated device quota per tenant, default 200"`

View File

@@ -58,7 +58,7 @@ func StartService() {
cron.AddJob1("CleanPendingDeleteServers", time.Duration(opts.PendingDeleteCheckSeconds)*time.Second, models.GuestManager.CleanPendingDeleteServers)
cron.AddJob1("CleanPendingDeleteDisks", time.Duration(opts.PendingDeleteCheckSeconds)*time.Second, models.DiskManager.CleanPendingDeleteDisks)
cron.AddJob1("CleanPendingDeleteLoadbalancers", time.Duration(opts.LoadbalancerPendingDeleteCheckInterval)*time.Second, models.LoadbalancerAgentManager.CleanPendingDeleteLoadbalancers)
cron.AddJob1("CleanPendingDeleteServers", time.Duration(opts.PrepaidExpireCheckSeconds)*time.Second, models.GuestManager.DeleteExpiredPrepaidServers)
cron.AddJob1("CleanExpiredPrepaidServers", time.Duration(opts.PrepaidExpireCheckSeconds)*time.Second, models.GuestManager.DeleteExpiredPrepaidServers)
cron.AddJob1("StartHostPingDetectionTask", time.Duration(opts.HostOfflineDetectionInterval)*time.Second, models.HostManager.PingDetectionTask)
cron.AddJob2("AutoDiskSnapshot", opts.AutoSnapshotDay, opts.AutoSnapshotHour, 0, 0, models.DiskManager.AutoDiskSnapshot, false)

View File

@@ -83,7 +83,19 @@ func rangeObjHandler(
}
projectName := json.GetAnyString(getQuery(r), []string{"project", "tenant"})
if projectName != "" {
userCred, err = generateProjectUserCred(ctx, userCred, projectName)
isAllow := false
if consts.IsRbacEnabled() {
result := policy.PolicyManager.Allow(true, userCred, consts.GetServiceType(),
policy.PolicyDelegation, policy.PolicyActionGet)
isAllow = result == rbacutils.AdminAllow
} else {
isAllow = userCred.IsAdminAllow(consts.GetServiceType(), policy.PolicyDelegation, policy.PolicyActionGet)
}
if !isAllow {
httperrors.ForbiddenError(w, "not allow to delegate query usage")
return
}
userCred, err = db.TenantCacheManager.GenerateProjectUserCred(ctx, projectName)
if err != nil {
httperrors.GeneralServerError(w, err)
return
@@ -102,19 +114,6 @@ func rangeObjHandler(
}
}
func generateProjectUserCred(ctx context.Context, userCred mcclient.TokenCredential, projectName string) (mcclient.TokenCredential, error) {
project, err := db.TenantCacheManager.FetchTenantByIdOrName(ctx, projectName)
if err != nil {
return nil, err
}
return &mcclient.SSimpleToken{
Domain: project.Domain,
DomainId: project.DomainId,
Project: project.Name,
ProjectId: project.Id,
}, nil
}
func addHandler(prefix, rangeObjKey string, hf appsrv.FilterHandler, app *appsrv.Application) {
ahf := auth.Authenticate(hf)
name := "get_usage"

View File

@@ -7,9 +7,13 @@ import (
"yunion.io/x/onecloud/pkg/util/httputils"
)
func HTTPError(w http.ResponseWriter, msg string, statusCode int, class string, error httputils.Error) {
func SendHTTPErrorHeader(w http.ResponseWriter, statusCode int) {
w.WriteHeader(statusCode)
w.Header().Set("Content-Type", "application/json")
}
func HTTPError(w http.ResponseWriter, msg string, statusCode int, class string, error httputils.Error) {
SendHTTPErrorHeader(w, statusCode)
body := jsonutils.NewDict()
body.Add(jsonutils.NewInt(int64(statusCode)), "code")
body.Add(jsonutils.NewString(msg), "details")

View File

@@ -0,0 +1,44 @@
package models
import "yunion.io/x/onecloud/pkg/cloudcommon/db"
type SImageMemberManager struct {
db.SResourceBaseManager
}
var ImageMemberManager *SImageMemberManager
func init() {
ImageMemberManager = &SImageMemberManager{
SResourceBaseManager: db.NewResourceBaseManager(
SImageMember{},
"image_members",
"image_member",
"image_members",
),
}
ImageMemberManager.TableSpec().AddIndex(true, "image_id", "member")
}
/*
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| image_id | varchar(36) | NO | MUL | NULL | |
| member | varchar(255) | NO | | NULL | |
| can_share | tinyint(1) | NO | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | YES | | NULL | |
| deleted_at | datetime | YES | | NULL | |
| deleted | tinyint(1) | NO | MUL | NULL | |
+------------+--------------+------+-----+---------+----------------+
*/
type SImageMember struct {
SImagePeripheral
Member string `width:"255" nullable:"false"`
CanShare bool `nullable:"false"`
}

View File

@@ -0,0 +1,10 @@
package models
import "yunion.io/x/onecloud/pkg/cloudcommon/db"
type SImagePeripheral struct {
db.SResourceBase
Id int `primary:"true" auto_increment:"true" nullable:"false"`
ImageId string `width:"36" index:"true" nullable:"false"`
}

View File

@@ -0,0 +1,124 @@
package models
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/mcclient"
)
type SImagePropertyManager struct {
db.SResourceBaseManager
}
var ImagePropertyManager *SImagePropertyManager
func init() {
ImagePropertyManager = &SImagePropertyManager{
SResourceBaseManager: db.NewResourceBaseManager(
SImageProperty{},
"image_properties",
"image_property",
"image_properties",
),
}
ImagePropertyManager.TableSpec().AddIndex(true, "image_id", "name")
}
/*
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| image_id | varchar(36) | NO | MUL | NULL | |
| name | varchar(255) | NO | | NULL | |
| value | text | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | YES | | NULL | |
| deleted_at | datetime | YES | | NULL | |
| deleted | tinyint(1) | NO | MUL | NULL | |
+------------+--------------+------+-----+---------+----------------+
*/
type SImageProperty struct {
SImagePeripheral
Name string `width:"255"`
Value string `nullable:"true" create:"optional"`
}
func (manager *SImagePropertyManager) GetProperties(imageId string) (map[string]string, error) {
properties := make([]SImageProperty, 0)
q := manager.Query("name", "value").Equals("image_id", imageId)
err := db.FetchModelObjects(manager, q, &properties)
if err != nil {
return nil, err
}
props := make(map[string]string)
for i := range properties {
props[properties[i].Name] = properties[i].Value
}
return props, nil
}
func (manager *SImagePropertyManager) SaveProperties(ctx context.Context, userCred mcclient.TokenCredential, imageId string, props jsonutils.JSONObject) error {
propsJson := props.(*jsonutils.JSONDict)
for _, k := range propsJson.SortedKeys() {
v, _ := propsJson.GetString(k)
_, err := manager.SaveProperty(ctx, userCred, imageId, k, v)
if err != nil {
return err
}
}
return nil
}
func (manager *SImagePropertyManager) SaveProperty(ctx context.Context, userCred mcclient.TokenCredential, imageId string, key string, value string) (*SImageProperty, error) {
prop, _ := manager.GetProperty(imageId, key)
if prop != nil {
if prop.Value != value {
return prop, prop.UpdateValue(ctx, userCred, value)
} else {
return prop, nil
}
} else {
// create
return manager.NewProperty(ctx, userCred, imageId, key, value)
}
}
func (manager *SImagePropertyManager) GetProperty(imageId string, key string) (*SImageProperty, error) {
q := manager.Query().Equals("image_id", imageId).Equals("name", key)
prop := SImageProperty{}
prop.SetModelManager(manager)
err := q.First(&prop)
if err != nil {
return nil, err
}
return &prop, nil
}
func (manager *SImagePropertyManager) NewProperty(ctx context.Context, userCred mcclient.TokenCredential, imageId string, key string, value string) (*SImageProperty, error) {
prop := SImageProperty{}
prop.ImageId = imageId
prop.Name = key
prop.Value = value
err := manager.TableSpec().Insert(&prop)
if err != nil {
return nil, err
}
prop.SetModelManager(manager)
return &prop, nil
}
func (self *SImageProperty) UpdateValue(ctx context.Context, userCred mcclient.TokenCredential, value string) error {
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
self.Value = value
return nil
})
return err
}

View File

@@ -0,0 +1,213 @@
package models
import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/qemuimg"
"fmt"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/image/torrent"
"yunion.io/x/onecloud/pkg/util/torrentutils"
"os"
)
type SImageSubformatManager struct {
db.SResourceBaseManager
}
var ImageSubformatManager *SImageSubformatManager
func init() {
ImageSubformatManager = &SImageSubformatManager{
SResourceBaseManager: db.NewResourceBaseManager(
SImageSubformat{},
"image_subformats",
"image_subformat",
"image_subformats",
),
}
ImageSubformatManager.TableSpec().AddIndex(true, "image_id", "format", "is_torrent")
}
type SImageSubformat struct {
SImagePeripheral
Format string `width:"20" charset:"ascii" nullable:"true"`
Size int64 `nullable:"false"`
Location string `nullable:"false"`
Checksum string `width:"32" charset:"ascii" nullable:"true"`
Status string `nullable:"false"`
TorrentSize int64 `nullable:"false"`
TorrentLocation string `nullable:"true"`
TorrentChecksum string `width:"32" charset:"ascii" nullable:"true"`
TorrentStatus string `nullable:"false"`
}
func (manager *SImageSubformatManager) FetchSubImage(id string, format string) *SImageSubformat {
q := manager.Query().Equals("image_id", id).Equals("format", format)
subImg := SImageSubformat{}
err := q.First(&subImg)
if err != nil {
log.Errorf("query subimage fail!")
return nil
}
return &subImg
}
func (manager *SImageSubformatManager) GetAllSubImages(id string) []SImageSubformat {
q := manager.Query().Equals("image_id", id)
var subImgs []SImageSubformat
err := db.FetchModelObjects(manager, q, &subImgs)
if err != nil {
log.Errorf("query subimage fail!")
return nil
}
return subImgs
}
func (self *SImageSubformat) DoConvert(image *SImage) error {
err := self.Save(image)
if err != nil {
log.Errorf("fail to convert image %s", err)
return err
}
err = self.SaveTorrent()
if err != nil {
log.Errorf("fail to convert image torrent %s", err)
return err
}
err = self.seedTorrent()
if err != nil {
log.Errorf("fail to seed torrent %s", err)
return err
}
return nil
}
func (self *SImageSubformat) Save(image *SImage) error {
if self.Status == IMAGE_STATUS_ACTIVE {
return nil
}
if self.Status != IMAGE_STATUS_QUEUED {
return nil // httperrors.NewInvalidStatusError("cannot save in status %s", self.Status)
}
location := image.GetPath(self.Format)
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
self.Status = IMAGE_STATUS_SAVING
self.Location = fmt.Sprintf("%s%s", LocalFilePrefix, location)
return nil
})
if err != nil {
log.Errorf("updateStatus fail %s", err)
return err
}
img, err := image.getQemuImage()
if err != nil {
log.Errorf("image.getQemuImage fail %s", err)
return err
}
nimg, err := img.Clone(location, qemuimg.TImageFormat(self.Format), true)
if err != nil {
log.Errorf("img.Clone fail %s", err)
return err
}
checksum, err := fileutils2.Md5(location)
if err != nil {
log.Errorf("fileutils2.Md5 fail %s", err)
return err
}
_, err = self.GetModelManager().TableSpec().Update(self, func() error {
self.Status = IMAGE_STATUS_ACTIVE
self.Location = fmt.Sprintf("%s%s", LocalFilePrefix, location)
self.Checksum = checksum
self.Size = nimg.ActualSizeBytes
return nil
})
if err != nil {
log.Errorf("updateStatus fail %s", err)
return err
}
return nil
}
func (self *SImageSubformat) SaveTorrent() error {
if self.TorrentStatus == IMAGE_STATUS_ACTIVE {
return nil
}
if self.TorrentStatus != IMAGE_STATUS_QUEUED {
return nil // httperrors.NewInvalidStatusError("cannot save torrent in status %s", self.Status)
}
imgPath := self.getLocalLocation()
torrentPath := fmt.Sprintf("%s.torrent", imgPath)
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
self.TorrentStatus = IMAGE_STATUS_SAVING
self.TorrentLocation = fmt.Sprintf("%s%s", LocalFilePrefix, torrentPath)
return nil
})
if err != nil {
log.Errorf("updateStatus fail %s", err)
return err
}
_, err = torrentutils.GenerateTorrent(imgPath, torrent.GetTrackers(), torrentPath)
if err != nil {
log.Errorf("torrentutils.GenerateTorrent fail %s", err)
return err
}
checksum, err := fileutils2.Md5(torrentPath)
if err != nil {
log.Errorf("fileutils2.Md5 fail %s", err)
return err
}
_, err = self.GetModelManager().TableSpec().Update(self, func() error {
self.TorrentStatus = IMAGE_STATUS_ACTIVE
self.TorrentLocation = fmt.Sprintf("%s%s", LocalFilePrefix, torrentPath)
self.TorrentChecksum = checksum
self.TorrentSize = fileutils2.FileSize(torrentPath)
return nil
})
if err != nil {
log.Errorf("updateStatus fail %s", err)
return err
}
return nil
}
func (self *SImageSubformat) getLocalLocation() string {
return self.Location[len(LocalFilePrefix):]
}
func (self *SImageSubformat) getLocalTorrentLocation() string {
return self.TorrentLocation[len(LocalFilePrefix):]
}
func (self *SImageSubformat) seedTorrent() error {
file := self.getLocalTorrentLocation()
log.Debugf("add torrent %s to seed...", file)
return torrent.AddTorrent(file)
}
func (self *SImageSubformat) StopTorrent() {
if len(self.TorrentLocation) > 0 {
torrent.RemoveTorrent(self.getLocalTorrentLocation())
}
}
func (self *SImageSubformat) RemoveFiles() error {
self.StopTorrent()
if len(self.TorrentLocation) > 0 {
err := os.Remove(self.getLocalTorrentLocation())
if err != nil {
return err
}
}
if len(self.Location) > 0 {
err := os.Remove(self.getLocalLocation())
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,40 @@
package models
import "yunion.io/x/onecloud/pkg/cloudcommon/db"
type SImageTagManager struct {
db.SResourceBaseManager
}
var ImageTagManager *SImageTagManager
func init() {
ImageTagManager = &SImageTagManager{
SResourceBaseManager: db.NewResourceBaseManager(
SImageTag{},
"image_tags",
"image_tag",
"image_tags",
),
}
ImageTagManager.TableSpec().AddIndex(true, "image_id", "value")
}
/*
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| image_id | varchar(36) | NO | MUL | NULL | |
| value | varchar(255) | NO | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | YES | | NULL | |
| deleted_at | datetime | YES | | NULL | |
| deleted | tinyint(1) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
*/
type SImageTag struct {
SImagePeripheral
Value string `width:"255" nullable:"false"`
}

758
pkg/image/models/images.go Normal file
View File

@@ -0,0 +1,758 @@
package models
import (
"context"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/timeutils"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/image/options"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/streamutils"
"yunion.io/x/pkg/tristate"
"yunion.io/x/onecloud/pkg/cloudcommon/qemuimg"
)
type TImageType string
const (
// https://docs.openstack.org/glance/pike/user/statuses.html
//
IMAGE_STATUS_QUEUED = "queued"
IMAGE_STATUS_SAVING = "saving"
IMAGE_STATUS_ACTIVE = "active"
IMAGE_STATUS_CONVERTING = "converting"
IMAGE_STATUS_DEACTIVATED = "deactivated"
IMAGE_STATUS_KILLED = "killed"
IMAGE_STATUS_DELETED = "deleted"
IMAGE_STATUS_PENDING_DELETE = "pending_delete"
ImageTypeTemplate = TImageType("image")
ImageTypeISO = TImageType("iso")
LocalFilePrefix = "file://"
)
var (
candidateSubImageFormats = []qemuimg.TImageFormat {qemuimg.QCOW2, qemuimg.VMDK}
)
type SImageManager struct {
db.SSharableVirtualResourceBaseManager
}
var ImageManager *SImageManager
var imgStreamingWorkerMan *appsrv.SWorkerManager
func init() {
ImageManager = &SImageManager{
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
SImage{},
"images",
"image",
"images",
),
}
imgStreamingWorkerMan = appsrv.NewWorkerManager("image_streaming_worker", 20, 1024)
}
/*
+------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------+-------+
| id | varchar(36) | NO | PRI | NULL | |
| name | varchar(255) | YES | | NULL | |
| size | bigint(20) | YES | | NULL | |
| status | varchar(30) | NO | | NULL | |
| is_public | tinyint(1) | NO | MUL | NULL | |
| location | text | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | YES | | NULL | |
| deleted_at | datetime | YES | | NULL | |
| deleted | tinyint(1) | NO | MUL | NULL | |
| parent_id | varchar(36) | YES | | NULL | |
| disk_format | varchar(20) | YES | | NULL | |
| container_format | varchar(20) | YES | | NULL | |
| checksum | varchar(32) | YES | | NULL | |
| owner | varchar(255) | YES | | NULL | |
| min_disk | int(11) | NO | | NULL | |
| min_ram | int(11) | NO | | NULL | |
| protected | tinyint(1) | YES | | NULL | |
| description | varchar(256) | YES | | NULL | |
+------------------+--------------+------+-----+---------+-------+
*/
type SImage struct {
db.SSharableVirtualResourceBase
Size int64 `nullable:"true" list:"user" create:"optional"`
VirtualSize int64 `nullable:"true" list:"user" create:"optional"`
Location string `nullable:"true"`
DiskFormat string `width:"20" charset:"ascii" nullable:"true" list:"user" create:"optional"` // Column(VARCHAR(32, charset='ascii'), nullable=False, default='qcow2')
Checksum string `width:"32" charset:"ascii" nullable:"true" get:"user"`
Owner string `width:"255" charset:"ascii" nullable:"true" get:"user"`
MinDisk int32 `nullable:"false" default:"0" get:"user" create:"optional" update:"user"`
MinRam int32 `nullable:"false" default:"0" get:"user" create:"optional" update:"user"`
Protected *bool `nullable:"true" get:"user" create:"optional" update:"user"`
}
func (manager *SImageManager) CustomizeHandlerInfo(info *appsrv.SHandlerInfo) {
switch info.GetName(nil) {
case "get_details", "create", "update":
info.SetProcessTimeout(time.Minute * 30).SetWorkerManager(imgStreamingWorkerMan)
}
}
func (manager *SImageManager) FetchCreateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
return modules.FetchImageMeta(header), nil
}
func (manager *SImageManager) FetchUpdateHeaderData(ctx context.Context, header http.Header) (jsonutils.JSONObject, error) {
return modules.FetchImageMeta(header), nil
}
func (manager *SImageManager) InitializeData() error {
// set cloudregion ID
images := make([]SImage, 0)
q := manager.Query().IsNullOrEmpty("tenant_id")
err := db.FetchModelObjects(manager, q, &images)
if err != nil {
return err
}
for i := 0; i < len(images); i += 1 {
if len(images[i].ProjectId) == 0 {
manager.TableSpec().Update(&images[i], func() error {
images[i].ProjectId = images[i].Owner
return nil
})
}
}
return nil
}
func (self *SImage) AllowGetDetailsTorrent(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowGetSpec(userCred, self, "torrent")
}
func (self *SImage) GetDetailsTorrent(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return nil, nil
}
func (manager *SImageManager) AllowGetPropertyDetail(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return true
}
func (manager *SImageManager) GetPropertyDetail(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
appParams := appsrv.AppContextGetParams(ctx)
appParams.OverrideResponseBodyWrapper = true
queryDict := query.(*jsonutils.JSONDict)
queryDict.Add(jsonutils.JSONTrue, "details")
items, err := db.ListItems(manager, ctx, userCred, queryDict, "")
if err != nil {
log.Errorf("Fail to list items: %s", err)
return nil, httperrors.NewGeneralError(err)
}
return modules.ListResult2JSONWithKey(items, manager.KeywordPlural()), nil
}
func (manager *SImageManager) IsCustomizedGetDetailsBody() bool {
return true
}
func (self *SImage) CustomizedGetDetailsBody(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
appParams := appsrv.AppContextGetParams(ctx)
filePath := self.GetPath("")
fp, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer fp.Close()
buf := make([]byte, 4096)
for {
n, _ := fp.Read(buf)
if n > 0 {
offset := 0
for offset < n {
m, err := appParams.Response.Write(buf[offset:n])
if err != nil {
return nil, err
}
offset += m
}
} else if n == 0 {
break
}
}
return nil, nil
}
func (self *SImage) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SVirtualResourceBase.GetCustomizeColumns(ctx, userCred, query)
properties, _ := ImagePropertyManager.GetProperties(self.Id)
if len(properties) > 0 {
jsonProps := jsonutils.NewDict()
for k, v := range properties {
jsonProps.Add(jsonutils.NewString(v), k)
}
extra.Add(jsonProps, "properties")
}
return extra
}
func (self *SImage) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
extra, err := self.SVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
if err != nil {
return nil, err
}
properties, err := ImagePropertyManager.GetProperties(self.Id)
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
propJson := jsonutils.NewDict()
for k, v := range properties {
propJson.Add(jsonutils.NewString(v), k)
}
extra.Add(propJson, "properties")
if self.PendingDeleted {
pendingDeletedAt := self.PendingDeletedAt.Add(time.Second * time.Duration(options.Options.PendingDeleteExpireSeconds))
extra.Add(jsonutils.NewString(timeutils.FullIsoTime(pendingDeletedAt)), "auto_delete_at")
}
return extra, nil
}
func (self *SImage) GetExtraDetailsHeaders(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) map[string]string {
headers := make(map[string]string)
extra, _ := self.SVirtualResourceBase.GetExtraDetails(ctx, userCred, query)
if extra != nil {
for _, k := range extra.SortedKeys() {
val, _ := extra.GetString(k)
if len(val) > 0 {
headers[fmt.Sprintf("%s%s", modules.IMAGE_META, k)] = val
}
}
}
jsonDict := jsonutils.Marshal(self).(*jsonutils.JSONDict)
fields := db.GetDetailFields(self.GetModelManager(), userCred)
for _, k := range jsonDict.SortedKeys() {
if utils.IsInStringArray(k, fields) {
val, _ := jsonDict.GetString(k)
if len(val) > 0 {
headers[fmt.Sprintf("%s%s", modules.IMAGE_META, k)] = val
}
}
}
properties, _ := ImagePropertyManager.GetProperties(self.Id)
if len(properties) > 0 {
for k, v := range properties {
headers[fmt.Sprintf("%s%s", modules.IMAGE_META_PROPERTY, k)] = v
}
}
if self.PendingDeleted {
pendingDeletedAt := self.PendingDeletedAt.Add(time.Second * time.Duration(options.Options.PendingDeleteExpireSeconds))
headers[fmt.Sprintf("%s%s", modules.IMAGE_META, "auto_delete_at")] = timeutils.FullIsoTime(pendingDeletedAt)
}
return headers
}
func (manager *SImageManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
_, err := manager.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data)
if err != nil {
return nil, err
}
pendingUsage := SQuota{Image: 1}
if err := QuotaManager.CheckSetPendingQuota(ctx, userCred, userCred.GetProjectId(), &pendingUsage); err != nil {
return nil, httperrors.NewOutOfQuotaError("%s", err)
}
return data, nil
}
func (self *SImage) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
err := self.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerProjId, query, data)
if err != nil {
return err
}
self.Status = IMAGE_STATUS_QUEUED
self.Owner = self.ProjectId
return nil
}
func (self *SImage) GetPath(format string) string {
path := filepath.Join(options.Options.FilesystemStoreDatadir, self.Id)
if len(format) > 0 {
path = fmt.Sprintf("%s.%s", path, format)
}
return path
}
func (self *SImage) OnSaveFailed(ctx context.Context, userCred mcclient.TokenCredential, msg string) {
log.Errorf(msg)
self.SetStatus(userCred, IMAGE_STATUS_QUEUED, msg)
db.OpsLog.LogEvent(self, db.ACT_SAVE_FAIL, msg, userCred)
logclient.AddActionLog(self, logclient.ACT_IMAGE_SAVE, nil, userCred, false)
}
func (self *SImage) OnSaveSuccess(ctx context.Context, userCred mcclient.TokenCredential, msg string) {
self.SetStatus(userCred, IMAGE_STATUS_ACTIVE, msg)
db.OpsLog.LogEvent(self, db.ACT_SAVE, msg, userCred)
logclient.AddActionLog(self, logclient.ACT_IMAGE_SAVE, nil, userCred, true)
}
func (self *SImage) SaveImageFromStream(reader io.Reader) error {
fp, err := os.Create(self.GetPath(""))
if err != nil {
return err
}
defer fp.Close()
sp, err := streamutils.StreamPipe(reader, fp)
if err != nil {
return err
}
virtualSize := int64(0)
format := ""
img, err := qemuimg.NewQemuImage(self.GetPath(""))
if err != nil {
return err
} else {
format = string(img.Format)
virtualSize = img.SizeBytes
}
self.GetModelManager().TableSpec().Update(self, func() error {
self.Size = sp.Size
self.Checksum = sp.CheckSum
self.Location = fmt.Sprintf("%s%s", LocalFilePrefix, self.GetPath(""))
if len(format) > 0 {
self.DiskFormat = format
}
if virtualSize > 0 {
self.VirtualSize = virtualSize
}
return nil
})
return nil
}
func (self *SImage) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) {
self.SVirtualResourceBase.PostCreate(ctx, userCred, ownerProjId, query, data)
pendingUsage := SQuota{Image: 1}
QuotaManager.CancelPendingUsage(ctx, userCred, userCred.GetProjectId(), &pendingUsage, &pendingUsage)
if data.Contains("properties") {
// update properties
props, _ := data.Get("properties")
err := ImagePropertyManager.SaveProperties(ctx, userCred, self.Id, props)
if err != nil {
log.Warningf("save properties error %s", err)
}
}
appParams := appsrv.AppContextGetParams(ctx)
if appParams.Request.ContentLength > 0 {
db.OpsLog.LogEvent(self, db.ACT_SAVING, "create upload", userCred)
self.SetStatus(userCred, IMAGE_STATUS_SAVING, "create upload")
err := self.SaveImageFromStream(appParams.Request.Body)
if err != nil {
self.OnSaveFailed(ctx, userCred, fmt.Sprintf("create upload fail %s", err))
return
}
self.OnSaveSuccess(ctx, userCred, "create upload success")
self.StartImageConvertTask(ctx, userCred, "")
} else {
copyFrom := appParams.Request.Header.Get(modules.IMAGE_META_COPY_FROM)
if len(copyFrom) > 0 {
self.startImageCopyFromUrlTask(ctx, userCred, copyFrom, "")
}
}
}
func (self *SImage) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
if self.Status != IMAGE_STATUS_QUEUED {
appParams := appsrv.AppContextGetParams(ctx)
if appParams != nil && appParams.Request.ContentLength > 0 {
return nil, httperrors.NewInvalidStatusError("cannot upload in status %s", self.Status)
}
} else {
appParams := appsrv.AppContextGetParams(ctx)
if appParams != nil {
if appParams.Request.ContentLength > 0 {
self.SetStatus(userCred, IMAGE_STATUS_SAVING, "update start upload")
err := self.SaveImageFromStream(appParams.Request.Body)
if err != nil {
self.OnSaveFailed(ctx, userCred, fmt.Sprintf("update upload failed %s", err))
return nil, httperrors.NewGeneralError(err)
}
self.OnSaveSuccess(ctx, userCred, "update upload success")
data.Remove("status")
} else {
copyFrom := appParams.Request.Header.Get(modules.IMAGE_META_COPY_FROM)
if len(copyFrom) > 0 {
err := self.startImageCopyFromUrlTask(ctx, userCred, copyFrom, "")
if err != nil {
self.OnSaveFailed(ctx, userCred, fmt.Sprintf("update copy from url failed %s", err))
return nil, httperrors.NewGeneralError(err)
}
}
}
}
}
return self.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, data)
}
func (self *SImage) PreUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
self.SVirtualResourceBase.PreUpdate(ctx, userCred, query, data)
}
func (self *SImage) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
self.SVirtualResourceBase.PostUpdate(ctx, userCred, query, data)
if data.Contains("properties") {
// update properties
props, _ := data.Get("properties")
err := ImagePropertyManager.SaveProperties(ctx, userCred, self.Id, props)
if err != nil {
log.Errorf("save properties error %s", err)
}
}
}
func (self *SImage) ValidateDeleteCondition(ctx context.Context) error {
if self.IsPublic {
return httperrors.NewInvalidStatusError("image is shared")
}
if self.Protected != nil && *self.Protected {
return httperrors.NewForbiddenError("image is protected")
}
return self.SVirtualResourceBase.ValidateDeleteCondition(ctx)
}
func (self *SImage) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
log.Infof("image delete do nothing")
return nil
}
func (self *SImage) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
return self.SVirtualResourceBase.Delete(ctx, userCred)
}
func (self *SImage) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
overridePendingDelete := false
purge := false
if query != nil {
overridePendingDelete = jsonutils.QueryBoolean(query, "override_pending_delete", false)
purge = jsonutils.QueryBoolean(query, "purge", false)
}
return self.startDeleteImageTask(ctx, userCred, "", purge, overridePendingDelete)
}
func (self *SImage) startDeleteImageTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, isPurge bool, overridePendingDelete bool) error {
params := jsonutils.NewDict()
if isPurge {
params.Add(jsonutils.JSONTrue, "purge")
}
if overridePendingDelete {
params.Add(jsonutils.JSONTrue, "override_pending_delete")
}
params.Add(jsonutils.NewString(self.Status), "image_status")
self.SetStatus(userCred, IMAGE_STATUS_DEACTIVATED, "")
task, err := taskman.TaskManager.NewTask(ctx, "ImageDeleteTask", self, userCred, params, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SImage) startImageCopyFromUrlTask(ctx context.Context, userCred mcclient.TokenCredential, copyFrom string, parentTaskId string) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(copyFrom), "copy_from")
msg := fmt.Sprintf("copy from url %s", copyFrom)
self.SetStatus(userCred, IMAGE_STATUS_SAVING, msg)
db.OpsLog.LogEvent(self, db.ACT_SAVING, msg, userCred)
task, err := taskman.TaskManager.NewTask(ctx, "ImageCopyFromUrlTask", self, userCred, params, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SImage) StartImageConvertTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "ImageConvertTask", self, userCred, nil, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SImage) AllowPerformCancelDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, self, "cancel-delete")
}
func (self *SImage) PerformCancelDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if self.PendingDeleted {
err := self.DoCancelPendingDelete(ctx, userCred)
return nil, err
}
return nil, nil
}
func (manager *SImageManager) getExpiredPendingDeleteDisks() []SImage {
deadline := time.Now().Add(time.Duration(options.Options.PendingDeleteExpireSeconds * -1) * time.Second)
q := manager.Query()
q = q.IsTrue("pending_deleted").LT("pending_deleted_at", deadline).Limit(options.Options.PendingDeleteMaxCleanBatchSize)
disks := make([]SImage, 0)
err := db.FetchModelObjects(ImageManager, q, &disks)
if err != nil {
log.Errorf("fetch disks error %s", err)
return nil
}
return disks
}
func (manager *SImageManager) CleanPendingDeleteImages(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
disks := manager.getExpiredPendingDeleteDisks()
if disks == nil {
return
}
for i := 0; i < len(disks); i += 1 {
disks[i].startDeleteImageTask(ctx, userCred, "", false, false)
}
}
func (self *SImage) DoPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
err := self.SVirtualResourceBase.DoPendingDelete(ctx, userCred)
if err != nil {
return err
}
_, err = self.GetModelManager().TableSpec().Update(self, func() error {
self.Status = IMAGE_STATUS_PENDING_DELETE
return nil
})
return err
}
func (self *SImage) DoCancelPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
err := self.SVirtualResourceBase.DoCancelPendingDelete(ctx, userCred)
if err != nil {
return err
}
_, err = self.GetModelManager().TableSpec().Update(self, func() error {
self.Status = IMAGE_STATUS_ACTIVE
return nil
})
return err
}
type SImageUsage struct {
Count int64
Size int64
}
func (manager *SImageManager) count(projectId string, isISO tristate.TriState, pendingDelete bool) SImageUsage {
images := manager.Query().SubQuery()
q := images.Query(sqlchemy.COUNT("count"), sqlchemy.SUM("size", images.Field("size")))
if len(projectId) > 0 {
q = q.Equals("tenant_id", projectId)
}
if pendingDelete {
q = q.IsTrue("pending_deleted")
} else {
q = q.IsFalse("pending_deleted")
}
if isISO.IsTrue() {
q = q.Equals("disk_format", "iso")
} else if isISO.IsFalse() {
q = q.NotEquals("disk_format", "iso")
}
usage := SImageUsage{}
q.First(&usage)
return usage
}
func (manager *SImageManager) Usage(projectId string, prefix string) map[string]int64 {
usages := make(map[string]int64)
count := manager.count(projectId, tristate.False, false)
usages[quotas.KeyName(prefix, "img.count")] = count.Count
usages[quotas.KeyName(prefix, "img.size")] = count.Size
count = manager.count(projectId, tristate.True, false)
usages[quotas.KeyName(prefix, "iso.count")] = count.Count
usages[quotas.KeyName(prefix, "iso.size")] = count.Size
count = manager.count(projectId, tristate.False, true)
usages[quotas.KeyName(prefix, "img.pending_delete.count")] = count.Count
usages[quotas.KeyName(prefix, "img.pending_delete.size")] = count.Size
count = manager.count(projectId, tristate.True, true)
usages[quotas.KeyName(prefix, "iso.pending_delete.count")] = count.Count
usages[quotas.KeyName(prefix, "iso.pending_delete.size")] = count.Size
return usages
}
func (self *SImage) GetImageType() TImageType {
if self.DiskFormat == string(qemuimg.ISO) {
return ImageTypeISO
} else {
return ImageTypeTemplate
}
}
func (self *SImage) MigrateSubImage() error {
subimg := ImageSubformatManager.FetchSubImage(self.Id, self.DiskFormat)
if subimg != nil {
return nil
}
subformat := SImageSubformat{}
subformat.SetModelManager(ImageSubformatManager)
subformat.ImageId = self.Id
subformat.Format = self.DiskFormat
subformat.Size = self.Size
subformat.Checksum = self.Checksum
subformat.Status = IMAGE_STATUS_ACTIVE
subformat.Location = self.Location
subformat.TorrentStatus = IMAGE_STATUS_QUEUED
err := ImageSubformatManager.TableSpec().Insert(&subformat)
if err != nil {
log.Errorf("fail to make subformat")
return err
}
return nil
}
func (self *SImage) MakeSubImages() error {
if self.GetImageType() == ImageTypeISO {
return nil
}
for _, format := range candidateSubImageFormats {
if string(format) != self.DiskFormat {
// need to create a record
subformat := ImageSubformatManager.FetchSubImage(self.Id, string(format))
if subformat == nil {
subformat := &SImageSubformat{}
subformat.SetModelManager(ImageSubformatManager)
subformat.ImageId = self.Id
subformat.Format = string(format)
subformat.Status = IMAGE_STATUS_QUEUED
subformat.TorrentStatus = IMAGE_STATUS_QUEUED
err := ImageSubformatManager.TableSpec().Insert(subformat)
if err != nil {
log.Errorf("fail to make subformat %s", format)
return err
}
}
}
}
return nil
}
func (self *SImage) ConvertAllSubformats() error {
subimgs := ImageSubformatManager.GetAllSubImages(self.Id)
for i := 0; i < len(subimgs); i += 1 {
err := subimgs[i].DoConvert(self)
if err != nil {
return err
}
}
return nil
}
func (self *SImage) getLocalLocation() string {
return self.Location[len(LocalFilePrefix):]
}
func (self *SImage) getQemuImage() (*qemuimg.SQemuImage, error) {
return qemuimg.NewQemuImage(self.getLocalLocation())
}
func (self *SImage) StopTorrents() {
subimgs := ImageSubformatManager.GetAllSubImages(self.Id)
for i := 0; i < len(subimgs); i += 1 {
subimgs[i].StopTorrent()
}
}
func (self *SImage) RemoveFiles() error {
subimgs := ImageSubformatManager.GetAllSubImages(self.Id)
for i := 0; i < len(subimgs); i += 1 {
subimgs[i].StopTorrent()
err := subimgs[i].RemoveFiles()
if err != nil {
return err
}
}
return os.Remove(self.getLocalLocation())
}
func (manager *SImageManager) getAllActiveImages() []SImage {
images := make([]SImage, 0)
q := manager.Query().Equals("status", IMAGE_STATUS_ACTIVE)
err := db.FetchModelObjects(manager, q, &images)
if err != nil {
log.Errorf("fail to query active images %s", err)
return nil
}
return images
}
func SeedTorrents() {
images := ImageManager.getAllActiveImages()
for i := 0; i < len(images); i += 1 {
log.Debugf("convert image subformats %s", images[i].Name)
// images[i].StartImageConvertTask(context.TODO(), auth.AdminCredential(), "")
}
}

View File

@@ -0,0 +1,23 @@
package models
import (
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
)
func InitDB() error {
for _, manager := range []db.IModelManager{
/*
* Important!!!
* initialization order matters, do not change the order
*/
ImageManager,
} {
err := manager.InitializeData()
if err != nil {
log.Errorf("Manager %s initializeData fail %s", manager.Keyword(), err)
// return err skip error table
}
}
return nil
}

View File

@@ -0,0 +1,79 @@
package models
import (
"errors"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/tristate"
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
"yunion.io/x/onecloud/pkg/image/options"
)
var QuotaManager *quotas.SQuotaManager
func init() {
dbStore := quotas.NewDBQuotaStore()
pendingStore := quotas.NewMemoryQuotaStore()
QuotaManager = quotas.NewQuotaManager("image-quotas", SQuota{}, dbStore, pendingStore)
}
var (
ErrOutOfImage = errors.New("out of image quota")
)
type SQuota struct {
Image int
}
func (self *SQuota) FetchSystemQuota() {
self.Image = options.Options.DefaultImageQuota
}
func (self *SQuota) FetchUsage(projectId string) error {
count := ImageManager.count(projectId, tristate.None, false)
self.Image = int(count.Count)
return nil
}
func (self *SQuota) IsEmpty() bool {
if self.Image > 0 {
return false
}
return true
}
func (self *SQuota) Add(quota quotas.IQuota) {
squota := quota.(*SQuota)
self.Image = self.Image + squota.Image
}
func (self *SQuota) Sub(quota quotas.IQuota) {
squota := quota.(*SQuota)
self.Image = quotas.NonNegative(self.Image - squota.Image)
}
func (self *SQuota) Update(quota quotas.IQuota) {
squota := quota.(*SQuota)
if squota.Image > 0 {
self.Image = squota.Image
}
}
func (self *SQuota) Exceed(request quotas.IQuota, quota quotas.IQuota) error {
sreq := request.(*SQuota)
squota := quota.(*SQuota)
if sreq.Image > 0 && self.Image > squota.Image {
return ErrOutOfImage
}
return nil
}
func (self *SQuota) ToJSON(prefix string) jsonutils.JSONObject {
ret := jsonutils.NewDict()
if self.Image > 0 {
ret.Add(jsonutils.NewInt(int64(self.Image)), quotas.KeyName(prefix, "image"))
}
return ret
}

View File

@@ -0,0 +1,24 @@
package options
import (
"yunion.io/x/onecloud/pkg/cloudcommon"
"yunion.io/x/onecloud/pkg/cloudcommon/pending_delete"
)
type SImageOptions struct {
cloudcommon.DBOptions
pending_delete.SPendingDeleteOptions
DefaultImageQuota int `default:"5" help:"Common image quota per tenant, default 5"`
PortV2 int `help:"Listening port for region V2"`
FilesystemStoreDatadir string `help:"Directory that the Filesystem backend store writes image data to"`
TorrentStoreDir string `help:"directory to store image torrent files"`
}
var (
Options SImageOptions
)

View File

@@ -0,0 +1,46 @@
package service
import (
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/appsrv/dispatcher"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/image/models"
"yunion.io/x/onecloud/pkg/image/usages"
)
const (
API_VERSION = "v1"
)
func initHandlers(app *appsrv.Application) {
db.InitAllManagers()
quotas.AddQuotaHandler(models.QuotaManager, API_VERSION, app)
usages.AddUsageHandler(API_VERSION, app)
taskman.AddTaskHandler(API_VERSION, app)
for _, manager := range []db.IModelManager{
taskman.TaskManager,
taskman.SubTaskManager,
taskman.TaskObjectManager,
// db.UserCacheManager,
db.TenantCacheManager,
db.Metadata,
models.ImageTagManager,
models.ImageMemberManager,
models.ImagePropertyManager,
} {
db.RegisterModelManager(manager)
}
for _, manager := range []db.IModelManager{
db.OpsLog,
models.ImageManager,
} {
db.RegisterModelManager(manager)
handler := db.NewModelHandler(manager)
dispatcher.AddModelDispatcher(API_VERSION, app, handler)
}
}

View File

@@ -0,0 +1,69 @@
package service
import (
"os"
_ "github.com/go-sql-driver/mysql"
"yunion.io/x/log"
_ "yunion.io/x/onecloud/pkg/image/tasks"
"time"
"yunion.io/x/onecloud/pkg/cloudcommon"
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
"yunion.io/x/onecloud/pkg/cloudcommon/cronman"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/image/models"
"yunion.io/x/onecloud/pkg/image/options"
"yunion.io/x/onecloud/pkg/image/torrent"
)
const (
SERVICE_TYPE = "image"
)
func StartService() {
consts.SetServiceType(SERVICE_TYPE)
cloudcommon.ParseOptions(&options.Options, &options.Options.Options, os.Args, "glance-api.conf")
if options.Options.PortV2 > 0 {
log.Infof("Port V2 %d is specified, use v2 port", options.Options.PortV2)
options.Options.Port = options.Options.PortV2
}
cloudcommon.InitAuth(&options.Options.Options, func() {
log.Infof("Auth complete!!")
})
cloudcommon.InitDB(&options.Options.DBOptions)
defer cloudcommon.CloseDB()
app := cloudcommon.InitApp(&options.Options.Options)
initHandlers(app)
err := torrent.InitTorrentClient()
if err != nil {
log.Errorf("fail to initialize torrent client: %s", err)
return
}
defer torrent.CloseTorrentClient()
if !db.CheckSync(options.Options.AutoSyncTable) {
log.Errorf("check sync failed")
return
}
models.InitDB()
models.SeedTorrents()
cron := cronman.GetCronJobManager()
cron.AddJob1("CleanPendingDeleteServers", time.Duration(options.Options.PendingDeleteCheckSeconds)*time.Second, models.ImageManager.CleanPendingDeleteImages)
cron.Start()
defer cron.Stop()
cloudcommon.ServeForever(app, &options.Options.Options)
}

View File

@@ -0,0 +1,57 @@
package tasks
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/image/models"
)
type ImageConvertTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(ImageConvertTask{})
}
func (self *ImageConvertTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
image := obj.(*models.SImage)
err := self.prepareConvert(image)
if err != nil {
self.SetStageFailed(ctx, "fail to convert subimages")
return
}
self.SetStage("OnConvertComplete", nil)
taskman.LocalTaskRun(self, func() (jsonutils.JSONObject, error) {
image.SetStatus(self.UserCred, models.IMAGE_STATUS_CONVERTING, "start convert")
defer image.SetStatus(self.UserCred, models.IMAGE_STATUS_ACTIVE, "convert failed")
err := image.ConvertAllSubformats()
return nil, err
})
}
func (self *ImageConvertTask) prepareConvert(image *models.SImage) error {
err := image.MigrateSubImage()
if err != nil {
return err
}
err = image.MakeSubImages()
if err != nil {
return err
}
return nil
}
func (self *ImageConvertTask) OnConvertComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
self.SetStageComplete(ctx, nil)
}
func (self *ImageConvertTask) OnConvertCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
self.SetStageFailed(ctx, data.String())
}

View File

@@ -0,0 +1,52 @@
package tasks
import (
"context"
"fmt"
"net/http"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/image/models"
"yunion.io/x/onecloud/pkg/util/httputils"
)
type ImageCopyFromUrlTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(ImageCopyFromUrlTask{})
}
func (self *ImageCopyFromUrlTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
image := obj.(*models.SImage)
copyFrom, _ := self.Params.GetString("copy_from")
header := http.Header{}
header.Set("Content-Type", "application/octet-stream")
resp, err := httputils.Request(nil, ctx, httputils.GET, copyFrom, header, nil, false)
if err != nil {
msg := fmt.Sprintf("copy from url %s request fail %s", copyFrom, err)
image.OnSaveFailed(ctx, self.UserCred, msg)
self.SetStageFailed(ctx, msg)
return
}
err = image.SaveImageFromStream(resp.Body)
if err != nil {
msg := fmt.Sprintf(" copy from url %s stream fail %s", copyFrom, err)
image.OnSaveFailed(ctx, self.UserCred, msg)
self.SetStageFailed(ctx, msg)
return
}
image.OnSaveSuccess(ctx, self.UserCred, "copy from success")
image.StartImageConvertTask(ctx, self.UserCred, "")
self.SetStageComplete(ctx, nil)
}

View File

@@ -0,0 +1,58 @@
package tasks
import (
"context"
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/image/models"
"yunion.io/x/onecloud/pkg/image/options"
)
type ImageDeleteTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(ImageDeleteTask{})
}
func (self *ImageDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
image := obj.(*models.SImage)
imageStatus, _ := self.Params.GetString("image_status")
isPurge := jsonutils.QueryBoolean(self.Params, "purge", false)
isOverridePendingDelete := jsonutils.QueryBoolean(self.Params, "override_pending_delete", false)
if options.Options.EnablePendingDelete && !image.PendingDeleted && imageStatus == models.IMAGE_STATUS_ACTIVE && !isPurge && !isOverridePendingDelete {
self.startPendingDeleteImage(ctx, image)
} else {
self.startDeleteImage(ctx, image)
}
}
func (self *ImageDeleteTask) startPendingDeleteImage(ctx context.Context, image *models.SImage) {
image.StopTorrents()
image.DoPendingDelete(ctx, self.UserCred)
self.SetStageComplete(ctx, nil)
}
func (self *ImageDeleteTask) startDeleteImage(ctx context.Context, image *models.SImage) {
log.Debugf("Delete image ....######")
err := image.RemoveFiles()
if err != nil {
msg := fmt.Sprintf("fail to remove %s %s", image.GetPath(""), err)
log.Errorf(msg)
self.SetStageFailed(ctx, msg)
return
}
image.SetStatus(self.UserCred, models.IMAGE_STATUS_DELETED, "delete")
image.RealDelete(ctx, self.UserCred)
self.SetStageComplete(ctx, nil)
}

View File

@@ -0,0 +1,85 @@
package torrent
import (
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/log"
"fmt"
"yunion.io/x/onecloud/pkg/image/options"
"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
)
var (
torrentClient *torrent.Client
torrentTable = make(map[string]*torrent.Torrent)
)
func GetTrackers() []string {
urls, err := auth.GetServiceURLs("torrent-tracker", options.Options.Region, "", "")
if err != nil {
log.Errorf("fail to get torrent-tracker")
return nil
}
return urls
}
func InitTorrentClient() error {
urls := GetTrackers()
if len(urls) == 0 {
log.Errorf("no valid torrent-tracker")
return fmt.Errorf("no valid torrent-tracker")
}
clientConfig := torrent.NewDefaultClientConfig()
clientConfig.Debug = false
clientConfig.Seed = true
clientConfig.DataDir = options.Options.FilesystemStoreDatadir
clientConfig.DisableTrackers = false
clientConfig.DisablePEX = false
clientConfig.NoDHT = true
client, err := torrent.NewClient(clientConfig)
if err != nil {
log.Errorf("error creating client: %s", err)
return err
}
torrentClient = client
return nil
}
func CloseTorrentClient() {
if torrentClient != nil {
torrentClient.Close()
}
}
func AddTorrent(filepath string) error {
mi, err := metainfo.LoadFromFile(filepath)
if err != nil {
log.Errorf("fail to open torrent file %s", err)
return err
}
t, err := torrentClient.AddTorrent(mi)
if err != nil {
log.Errorf("AddTorrent fail %s", err)
return err
}
torrentTable[filepath] = t
go func() {
<-t.GotInfo()
t.DownloadAll()
}()
return nil
}
func RemoveTorrent(filepath string) {
if t, ok := torrentTable[filepath]; ok {
t.Drop()
delete(torrentTable, filepath)
}
}

View File

@@ -0,0 +1,99 @@
package usages
import (
"context"
"fmt"
"net/http"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/appsrv"
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/image/models"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/util/rbacutils"
)
func AddUsageHandler(prefix string, app *appsrv.Application) {
prefix = fmt.Sprintf("%s/usages", prefix)
app.AddHandler2("GET", prefix, auth.Authenticate(ReportGeneralUsage), nil, "get_usage", nil)
}
func ReportGeneralUsage(ctx context.Context, w http.ResponseWriter, r *http.Request) {
_, query, _ := appsrv.FetchEnv(ctx, w, r)
userCred := auth.FetchUserCredential(ctx, policy.FilterPolicyCredential)
projectName := jsonutils.GetAnyString(query, []string{"project", "tenant"})
if projectName != "" {
isAllow := false
if consts.IsRbacEnabled() {
result := policy.PolicyManager.Allow(true, userCred, consts.GetServiceType(),
policy.PolicyDelegation, policy.PolicyActionGet)
isAllow = result == rbacutils.AdminAllow
} else {
isAllow = userCred.IsAdminAllow(consts.GetServiceType(), policy.PolicyDelegation, policy.PolicyActionGet)
}
if !isAllow {
httperrors.ForbiddenError(w, "not allow to delegate query usage")
return
}
var err error
userCred, err = db.TenantCacheManager.GenerateProjectUserCred(ctx, projectName)
if err != nil {
httperrors.GeneralServerError(w, err)
return
}
}
isAdmin := false
if consts.IsRbacEnabled() {
if policy.PolicyManager.Allow(true, userCred, consts.GetServiceType(),
"usages", policy.PolicyActionGet) == rbacutils.AdminAllow {
isAdmin = true
}
} else {
isAdmin = userCred.IsAdminAllow(consts.GetServiceType(), "usages", policy.PolicyActionGet)
}
var adminUsage map[string]int64
var projectUsage map[string]int64
if isAdmin {
adminUsage = models.ImageManager.Usage(userCred.GetProjectId(), "all")
}
isProject := false
if consts.IsRbacEnabled() {
if policy.PolicyManager.Allow(false, userCred, consts.GetServiceType(),
"usages", policy.PolicyActionGet) == rbacutils.Deny {
isProject = false
} else {
isProject = true
}
} else {
isProject = true
}
if isProject {
projectUsage = models.ImageManager.Usage(userCred.GetProjectId(), "")
}
if !isAdmin && !isProject {
httperrors.ForbiddenError(w, "not allow to get usage")
return
}
usages := jsonutils.NewDict()
if isProject {
usages.Update(jsonutils.Marshal(projectUsage))
}
if isAdmin {
usages.Update(jsonutils.Marshal(adminUsage))
}
body := jsonutils.NewDict()
body.Add(usages, "usage")
appsrv.SendJSON(w, body)
}

View File

@@ -162,10 +162,14 @@ func (a *authManager) reAuth() {
time.AfterFunc(time.Duration(duration.Nanoseconds()/2), a.reAuth)
}
func (a *authManager) getServiceURL(service, region, zone, endpointType string) (string, error) {
func (a *authManager) GetServiceURL(service, region, zone, endpointType string) (string, error) {
return a.adminCredential.GetServiceURL(service, region, zone, endpointType)
}
func (a *authManager) GetServiceURLs(service, region, zone, endpointType string) ([]string, error) {
return a.adminCredential.GetServiceURLs(service, region, zone, endpointType)
}
func (a *authManager) getTokenString() string {
return a.adminCredential.GetTokenString()
}
@@ -203,7 +207,11 @@ func Verify(tokenId string) (mcclient.TokenCredential, error) {
}
func GetServiceURL(service, region, zone, endpointType string) (string, error) {
return manager.getServiceURL(service, region, zone, endpointType)
return manager.GetServiceURL(service, region, zone, endpointType)
}
func GetServiceURLs(service, region, zone, endpointType string) ([]string, error) {
return manager.GetServiceURLs(service, region, zone, endpointType)
}
func GetTokenString() string {

View File

@@ -93,11 +93,11 @@ func joinUrl(baseUrl, path string) string {
return fmt.Sprintf("%s%s", baseUrl, path)
}
func (this *Client) rawRequest(ctx context.Context, endpoint string, token string, method string, url string, header http.Header, body io.Reader) (*http.Response, error) {
func (this *Client) rawRequest(ctx context.Context, endpoint string, token string, method httputils.THttpMethod, url string, header http.Header, body io.Reader) (*http.Response, error) {
return httputils.Request(this.httpconn, ctx, method, joinUrl(endpoint, url), getDefaultHeader(header, token), body, this.debug)
}
func (this *Client) jsonRequest(ctx context.Context, endpoint string, token string, method string, url string, header http.Header, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
func (this *Client) jsonRequest(ctx context.Context, endpoint string, token string, method httputils.THttpMethod, url string, header http.Header, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
/*bodystr := ""
if body != nil {
bodystr = body.String()

View File

@@ -67,7 +67,7 @@ func (this *BaseManager) versionedURL(path string) string {
}
func (this *BaseManager) jsonRequest(session *mcclient.ClientSession,
method string, path string,
method httputils.THttpMethod, path string,
header http.Header, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
return session.JSONVersionRequest(this.serviceType, this.endpointType,
method, this.versionedURL(path),
@@ -75,7 +75,7 @@ func (this *BaseManager) jsonRequest(session *mcclient.ClientSession,
}
func (this *BaseManager) rawRequest(session *mcclient.ClientSession,
method string, path string,
method httputils.THttpMethod, path string,
header http.Header, body io.Reader) (*http.Response, error) {
return session.RawVersionRequest(this.serviceType, this.endpointType,
method, this.versionedURL(path),
@@ -148,7 +148,7 @@ func (this *BaseManager) _list(session *mcclient.ClientSession, path, responseKe
return &ListResult{rets, int(total), int(limit), int(offset)}, nil
}
func (this *BaseManager) _submit(session *mcclient.ClientSession, method string, path string, body jsonutils.JSONObject, respKey string) (jsonutils.JSONObject, error) {
func (this *BaseManager) _submit(session *mcclient.ClientSession, method httputils.THttpMethod, path string, body jsonutils.JSONObject, respKey string) (jsonutils.JSONObject, error) {
hdr, resp, e := this.jsonRequest(session, method, path, nil, body)
if e != nil {
return nil, e
@@ -211,7 +211,7 @@ func SubmitResults2ListResult(results []SubmitResult) *ListResult {
return &ListResult{Data: arr, Total: len(arr), Limit: 0, Offset: 0}
}
func (this *BaseManager) _batch(session *mcclient.ClientSession, method string, path string, ids []string, body jsonutils.JSONObject, respKey string) []SubmitResult {
func (this *BaseManager) _batch(session *mcclient.ClientSession, method httputils.THttpMethod, path string, ids []string, body jsonutils.JSONObject, respKey string) []SubmitResult {
return BatchDo(ids, func(id string) (jsonutils.JSONObject, error) {
u := fmt.Sprintf(path, url.PathEscape(id))
return this._submit(session, method, u, body, respKey)

View File

@@ -7,6 +7,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/httputils"
)
var RawResource *RawResourceManager
@@ -49,7 +50,7 @@ type RawResourceManager struct {
serviceType string
}
func (m *RawResourceManager) request(s *mcclient.ClientSession, method string, path string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
func (m *RawResourceManager) request(s *mcclient.ClientSession, method httputils.THttpMethod, path string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
_, ret, err := s.JSONRequest(m.serviceType, "", method, path, nil, body)
return ret, err
}

View File

@@ -23,6 +23,8 @@ type ImageManager struct {
const (
IMAGE_META = "X-Image-Meta-"
IMAGE_META_PROPERTY = "X-Image-Meta-Property-"
IMAGE_META_COPY_FROM = "x-glance-api-copy-from"
)
func decodeMeta(str string) string {
@@ -34,14 +36,22 @@ func decodeMeta(str string) string {
}
}
func fetchImageMeta(h http.Header) jsonutils.JSONObject {
func FetchImageMeta(h http.Header) jsonutils.JSONObject {
meta := jsonutils.NewDict()
meta.Add(jsonutils.NewDict(), "properties")
for k, v := range h {
if len(k) > len(IMAGE_META_PROPERTY) && k[:len(IMAGE_META_PROPERTY)] == IMAGE_META_PROPERTY {
meta.Add(jsonutils.NewString(decodeMeta(v[0])), "properties", strings.ToLower(k[len(IMAGE_META_PROPERTY):]))
} else if len(k) > len(IMAGE_META) && k[:len(IMAGE_META)] == IMAGE_META {
meta.Add(jsonutils.NewString(decodeMeta(v[0])), strings.ToLower(k[len(IMAGE_META):]))
if strings.HasPrefix(k, IMAGE_META_PROPERTY) {
k := strings.ToLower(k[len(IMAGE_META_PROPERTY):])
meta.Add(jsonutils.NewString(decodeMeta(v[0])), "properties", k)
if strings.IndexByte(k, '-') > 0 {
meta.Add(jsonutils.NewString(decodeMeta(v[0])), "properties", strings.Replace(k, "-", "_", -1))
}
} else if strings.HasPrefix(k, IMAGE_META) {
k := strings.ToLower(k[len(IMAGE_META):])
meta.Add(jsonutils.NewString(decodeMeta(v[0])), k)
if strings.IndexByte(k, '-') > 0 {
meta.Add(jsonutils.NewString(decodeMeta(v[0])), strings.Replace(k, "-", "_", -1))
}
}
}
return meta
@@ -59,7 +69,7 @@ func (this *ImageManager) GetById(session *mcclient.ClientSession, id string, pa
if e != nil {
return nil, e
}
return fetchImageMeta(h), nil
return FetchImageMeta(h), nil
}
func (this *ImageManager) GetByName(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
@@ -445,7 +455,7 @@ func (this *ImageManager) _create(s *mcclient.ClientSession, params jsonutils.JS
}
imageId, _ := params.GetString("image_id")
path := fmt.Sprintf("/%s", this.URLPath())
method := "POST"
method := httputils.POST
if len(imageId) == 0 {
osType, err := params.GetString("properties", "os_type")
if err != nil {
@@ -481,7 +491,7 @@ func (this *ImageManager) _create(s *mcclient.ClientSession, params jsonutils.JS
}
body = nil
size = 0
headers.Set("x-glance-api-copy-from", copyFromUrl)
headers.Set(IMAGE_META_COPY_FROM, copyFromUrl)
}
headers.Set(fmt.Sprintf("%s%s", IMAGE_META, utils.Capitalize("container-format")), "bare")
if body != nil {
@@ -542,7 +552,7 @@ func (this *ImageManager) Download(s *mcclient.ClientSession, id string) (jsonut
path := fmt.Sprintf("/%s/%s", this.URLPath(), url.PathEscape(id))
resp, err := this.rawRequest(s, "GET", path, nil, nil)
if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
return fetchImageMeta(resp.Header), resp.Body, nil
return FetchImageMeta(resp.Header), resp.Body, nil
} else {
_, _, err = s.ParseJSONResponse(resp, err)
return nil, nil, err
@@ -560,7 +570,35 @@ func init() {
"OS_Distribution", "OS_version",
"Min_disk", "Min_ram", "Status",
"Notes", "OS_arch", "Preference",
"OS_Codename", "Parent_id", "Description"},
"OS_Codename", "Description"},
[]string{"Owner", "Owner_name"})}
register(&Images)
registerV2(&Images)
}
type SImageUsageManager struct {
ResourceManager
}
func (this *SImageUsageManager) GetUsage(session *mcclient.ClientSession, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
url := "/usages"
if params != nil {
query := params.QueryString()
if len(query) > 0 {
url = fmt.Sprintf("%s?%s", url, query)
}
}
return this._get(session, url, "usage")
}
var (
ImageUsages SImageUsageManager
)
func init() {
ImageUsages = SImageUsageManager{NewImageManager("image-usage", "image-usages",
[]string{},
[]string{})}
registerV2(&ImageUsages)
}

View File

@@ -38,7 +38,7 @@ func (this *QuotaManager) DoQuotaSet(s *mcclient.ClientSession, params jsonutils
}
data := quotas.Copy("tenant", "user")
body := jsonutils.NewDict()
body.Add(data, "quotas")
body.Add(data, this.KeywordPlural)
return this._post(s, url, body, this.KeywordPlural)
}
@@ -51,12 +51,13 @@ func (this *QuotaManager) DoQuotaCheck(s *mcclient.ClientSession, params jsonuti
}
data := quotas.Copy("tenant", "user")
body := jsonutils.NewDict()
body.Add(data, "quotas")
body.Add(data, this.KeywordPlural)
return this._post(s, url, body, this.KeywordPlural)
}
var (
Quotas QuotaManager
Quotas QuotaManager
ImageQuotas QuotaManager
)
func init() {
@@ -64,4 +65,9 @@ func init() {
[]string{},
[]string{})}
registerCompute(&Quotas)
ImageQuotas = QuotaManager{NewImageManager("image-quota", "image-quotas",
[]string{},
[]string{})}
registerV2(&ImageQuotas)
}

View File

@@ -217,7 +217,7 @@ func _getModule(session *mcclient.ClientSession, name string) (BaseManagerInterf
}
mods, ok := modtable[name]
if !ok {
return nil, fmt.Errorf("No such module: %s", name)
return nil, fmt.Errorf("No such module %s for version %s", name, session.GetApiVersion())
}
for _, mod := range mods {
url, e := session.GetServiceURL(mod.ServiceType(), mod.EndpointType())

View File

@@ -20,6 +20,10 @@ func register(mod BaseManagerInterface) {
_register("v1", mod)
}
func registerV2(mod BaseManagerInterface) {
_register("v2", mod)
}
func Register(mod BaseManagerInterface) {
register(mod)
}

View File

@@ -27,7 +27,7 @@ const (
)
var (
MutilVersionService = []string{"compute"}
MutilVersionService = []string{"compute", "image"}
ApiVersionByModule = true
)
@@ -157,7 +157,7 @@ func (this *ClientSession) getBaseUrl(service, endpointType, apiVersion string)
}
func (this *ClientSession) RawVersionRequest(
service, endpointType, method, url string,
service, endpointType string, method httputils.THttpMethod, url string,
headers http.Header, body io.Reader,
apiVersion string,
) (*http.Response, error) {
@@ -179,12 +179,12 @@ func (this *ClientSession) RawVersionRequest(
method, url, tmpHeader, body)
}
func (this *ClientSession) RawRequest(service, endpointType, method, url string, headers http.Header, body io.Reader) (*http.Response, error) {
func (this *ClientSession) RawRequest(service, endpointType string, method httputils.THttpMethod, url string, headers http.Header, body io.Reader) (*http.Response, error) {
return this.RawVersionRequest(service, endpointType, method, url, headers, body, "")
}
func (this *ClientSession) JSONVersionRequest(
service, endpointType, method, url string,
service, endpointType string, method httputils.THttpMethod, url string,
headers http.Header, body jsonutils.JSONObject,
apiVersion string,
) (http.Header, jsonutils.JSONObject, error) {
@@ -206,7 +206,7 @@ func (this *ClientSession) JSONVersionRequest(
method, url, tmpHeader, body)
}
func (this *ClientSession) JSONRequest(service, endpointType, method, url string, headers http.Header, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
func (this *ClientSession) JSONRequest(service, endpointType string, method httputils.THttpMethod, url string, headers http.Header, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
return this.JSONVersionRequest(service, endpointType, method, url, headers, body, "")
}

View File

@@ -0,0 +1,29 @@
package fileutils2
import (
"regexp"
"os/exec"
"yunion.io/x/log"
)
const (
blkidTypePattern = `TYPE="(?P<type>\w+)"`
)
var (
blkidTypeRegexp = regexp.MustCompile(blkidTypePattern)
)
func GetBlkidType(filepath string) string {
cmd := exec.Command("blkid", filepath)
out, err := cmd.CombinedOutput()
if err != nil {
log.Errorf("blkid fail %s %s", filepath, err)
return ""
}
matches := blkidTypeRegexp.FindStringSubmatch(string(out))
if len(matches) > 1 {
return matches[1]
}
return ""
}

View File

@@ -0,0 +1,27 @@
package fileutils2
import "testing"
func TestGetBlkidType(t *testing.T) {
cases := []struct {
In string
Want string
}{
{
`/dev/sda2: UUID="87a523a8-b382-4b45-a291-7ae56a13c99a" TYPE="ext4" PARTLABEL="Linux" PARTUUID="d9ac3dd7-da80-4c57-a701-e37956c07687"`,
"ext4",
},
{
`/opt/isoimage/iso/yunion-20180622.iso: UUID="2018-06-22-23-04-12-00" LABEL="CDROM" TYPE="iso9660" PTTYPE="dos"`,
"iso9660",
},
}
for _, c := range cases {
matches := blkidTypeRegexp.FindStringSubmatch(c.In)
if len(matches) > 1 && matches[1] == c.Want {
t.Logf("%s", matches)
} else {
t.Errorf("fail")
}
}
}

Some files were not shown because too many files have changed in this diff Show More