Files
cloudpods/pkg/compute/models/storagecachedimages.go
T
2018-12-12 23:24:34 +08:00

334 lines
11 KiB
Go

package models
import (
"context"
"fmt"
"time"
"github.com/serialx/hashring"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)
const (
CACHED_IMAGE_STATUS_INIT = "init"
CACHED_IMAGE_STATUS_SAVING = "saving"
CACHED_IMAGE_STATUS_CACHING = "caching"
CACHED_IMAGE_STATUS_READY = "ready"
CACHED_IMAGE_STATUS_DELETING = "deleting"
CACHED_IMAGE_STATUS_CACHE_FAILED = "cache_fail"
DOWNLOAD_SESSION_LENGTH = 3600 * 3 // 3 hour
)
type SStoragecachedimageManager struct {
db.SJointResourceBaseManager
}
var StoragecachedimageManager *SStoragecachedimageManager
func init() {
db.InitManager(func() {
StoragecachedimageManager = &SStoragecachedimageManager{
SJointResourceBaseManager: db.NewJointResourceBaseManager(
SStoragecachedimage{},
"storagecachedimages_tbl",
"storagecachedimage",
"storagecachedimages",
StoragecacheManager,
CachedimageManager,
),
}
})
}
type SStoragecachedimage struct {
db.SJointResourceBase
StoragecacheId string `width:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required" key_index:"true"`
CachedimageId string `width:"36" charset:"ascii" nullable:"false" list:"admin" create:"admin_required" key_index:"true"`
ExternalId string `width:"256" charset:"utf8" nullable:"false" get:"admin"`
Status string `width:"32" charset:"ascii" nullable:"false" default:"init" list:"admin" update:"admin" create:"admin_required"` // = Column(VARCHAR(32, charset='ascii'), nullable=False,
Path string `width:"256" charset:"utf8" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` // = Column(VARCHAR(256, charset='utf8'), nullable=True)
LastDownload time.Time `get:"admin"` // = Column(DateTime)
DownloadRefcnt int `get:"admin"` // = Column(Integer)
}
func (joint *SStoragecachedimage) Master() db.IStandaloneModel {
return db.JointMaster(joint)
}
func (joint *SStoragecachedimage) Slave() db.IStandaloneModel {
return db.JointSlave(joint)
}
func (self *SStoragecachedimageManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return db.IsAdminAllowList(userCred, self)
}
func (self *SStoragecachedimageManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowCreate(userCred, self)
}
func (self *SStoragecachedimage) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return db.IsAdminAllowGet(userCred, self)
}
func (self *SStoragecachedimage) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
return db.IsAdminAllowUpdate(userCred, self)
}
func (self *SStoragecachedimage) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return db.IsAdminAllowDelete(userCred, self)
}
func (self *SStoragecachedimage) getStorageHostId() (string, error) {
var s SStorage
storage := StorageManager.Query()
err := storage.Filter(sqlchemy.Equals(storage.Field("storagecache_id"), self.StoragecacheId)).First(&s)
if err != nil {
return "", err
}
hosts := s.GetAllAttachingHosts()
var hostIds = make([]string, 0)
for _, host := range hosts {
hostIds = append(hostIds, host.Id)
}
if len(hostIds) == 0 {
return "", nil
}
ring := hashring.New(hostIds)
ret, _ := ring.GetNode(self.StoragecacheId)
return ret, nil
}
func (self *SStoragecachedimage) GetHost() (*SHost, error) {
hostId, err := self.getStorageHostId()
if err != nil {
return nil, err
} else if len(hostId) == 0 {
return nil, nil
}
host, err := HostManager.FetchById(hostId)
if err != nil {
return nil, err
} else if host == nil {
return nil, nil
}
h, _ := host.(*SHost)
return h, nil
}
func (self *SStoragecachedimage) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
extra := self.SJointResourceBase.GetCustomizeColumns(ctx, userCred, query)
extra = db.JointModelExtra(self, extra)
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)
extra = db.JointModelExtra(self, extra)
return self.getExtraDetails(extra)
}
func (manager *SStoragecachedimageManager) AllowListDescendent(ctx context.Context, userCred mcclient.TokenCredential, model db.IStandaloneModel, query jsonutils.JSONObject) bool {
return db.IsAdminAllowList(userCred, manager)
}
func (self *SStoragecachedimage) GetCachedimage() *SCachedimage {
cachedImage, _ := CachedimageManager.FetchById(self.CachedimageId)
if cachedImage != nil {
return cachedImage.(*SCachedimage)
}
return nil
}
func (self *SStoragecachedimage) GetStoragecache() *SStoragecache {
cache, _ := StoragecacheManager.FetchById(self.StoragecacheId)
if cache != nil {
return cache.(*SStoragecache)
}
return nil
}
func (self *SStoragecachedimage) getExtraDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict {
storagecache := self.GetStoragecache()
if storagecache != nil {
extra.Add(jsonutils.NewStringArray(storagecache.getStorageNames()), "storages")
}
cachedImage := self.GetCachedimage()
if cachedImage != nil {
extra.Add(jsonutils.NewString(cachedImage.GetName()), "image")
extra.Add(jsonutils.NewInt(cachedImage.Size), "size")
}
extra.Add(jsonutils.NewInt(int64(self.getReferenceCount())), "reference")
return extra
}
func (self *SStoragecachedimage) getCdromReferenceCount() int {
// TODO
return 0
}
func (self *SStoragecachedimage) getDiskReferenceCount() int {
guestdisks := GuestdiskManager.Query().SubQuery()
disks := DiskManager.Query().SubQuery()
storages := StorageManager.Query().SubQuery()
q := guestdisks.Query()
q = q.Join(disks, sqlchemy.AND(sqlchemy.Equals(disks.Field("id"), guestdisks.Field("disk_id")),
sqlchemy.IsFalse(disks.Field("deleted"))))
q = q.Join(storages, sqlchemy.AND(sqlchemy.Equals(disks.Field("storage_id"), storages.Field("id")),
sqlchemy.IsFalse(storages.Field("deleted"))))
q = q.Filter(sqlchemy.Equals(storages.Field("storagecache_id"), self.StoragecacheId))
q = q.Filter(sqlchemy.Equals(disks.Field("template_id"), self.CachedimageId))
q = q.Filter(sqlchemy.NOT(sqlchemy.In(disks.Field("status"), []string{DISK_ALLOC_FAILED, DISK_INIT})))
return q.Count()
}
func (self *SStoragecachedimage) getReferenceCount() int {
return self.getCdromReferenceCount() + self.getDiskReferenceCount()
}
func (manager *SStoragecachedimageManager) GetStoragecachedimage(cacheId string, imageId string) *SStoragecachedimage {
obj, err := manager.FetchByIds(cacheId, imageId)
if err != nil {
log.Errorf("%s", err)
return nil
}
return obj.(*SStoragecachedimage)
}
func (self *SStoragecachedimage) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
return db.DeleteModel(ctx, userCred, self)
}
func (self *SStoragecachedimage) Detach(ctx context.Context, userCred mcclient.TokenCredential) error {
return db.DetachJoint(ctx, userCred, self)
}
func (self *SStoragecachedimage) ValidateDeleteCondition(ctx context.Context) error {
if self.getReferenceCount() > 0 {
return httperrors.NewNotEmptyError("Image is in use")
}
if !self.isDownloadSessionExpire() {
return httperrors.NewResourceBusyError("Active download session not expired")
}
image := self.GetCachedimage()
if !image.canDeleteLastCache() {
return httperrors.NewResourceBusyError("Cannot delete the last cache")
}
return self.SJointResourceBase.ValidateDeleteCondition(ctx)
}
func (self *SStoragecachedimage) isDownloadSessionExpire() bool {
if !self.LastDownload.IsZero() && time.Now().Sub(self.LastDownload) < DOWNLOAD_SESSION_LENGTH {
return false
} else {
return true
}
}
func (self *SStoragecachedimage) markDeleting(ctx context.Context, userCred mcclient.TokenCredential, isForce bool) error {
err := self.ValidateDeleteCondition(ctx)
if err != nil {
return err
}
cache := self.GetStoragecache()
image := self.GetCachedimage()
lockman.LockJointObject(ctx, cache, image)
defer lockman.ReleaseJointObject(ctx, cache, image)
if !isForce && !utils.IsInStringArray(self.Status,
[]string{CACHED_IMAGE_STATUS_READY, CACHED_IMAGE_STATUS_DELETING, CACHED_IMAGE_STATUS_CACHE_FAILED}) {
return httperrors.NewInvalidStatusError("Cannot uncache in status %s", self.Status)
}
_, err = self.GetModelManager().TableSpec().Update(self, func() error {
self.Status = CACHED_IMAGE_STATUS_DELETING
return nil
})
return err
}
func (manager *SStoragecachedimageManager) Register(ctx context.Context, userCred mcclient.TokenCredential, cacheId, imageId string) *SStoragecachedimage {
lockman.LockClass(ctx, manager, userCred.GetProjectId())
defer lockman.ReleaseClass(ctx, manager, userCred.GetProjectId())
cachedimage := manager.GetStoragecachedimage(cacheId, imageId)
if cachedimage != nil {
return cachedimage
}
cachedimage = &SStoragecachedimage{}
cachedimage.SetModelManager(manager)
cachedimage.StoragecacheId = cacheId
cachedimage.CachedimageId = imageId
cachedimage.Status = CACHED_IMAGE_STATUS_INIT
err := manager.TableSpec().Insert(cachedimage)
if err != nil {
log.Errorf("insert error %s", err)
return nil
}
return cachedimage
}
func (self *SStoragecachedimage) SetStatus(userCred mcclient.TokenCredential, status string, reason string) error {
if self.Status == status {
return nil
}
oldStatus := self.Status
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
self.Status = status
return nil
})
if err != nil {
return err
}
if userCred != nil {
notes := fmt.Sprintf("%s=>%s", oldStatus, status)
if len(reason) > 0 {
notes = fmt.Sprintf("%s: %s", notes, reason)
}
db.OpsLog.LogEvent(self, db.ACT_UPDATE_STATUS, notes, userCred)
}
return nil
}
func (self *SStoragecachedimage) AddDownloadRefcount() error {
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
self.DownloadRefcnt += 1
self.LastDownload = time.Now()
return nil
})
return err
}
func (self *SStoragecachedimage) SetExternalId(externalId string) error {
_, err := self.GetModelManager().TableSpec().Update(self, func() error {
self.ExternalId = externalId
return nil
})
return err
}