Files
cloudpods/pkg/compute/models/storagecaches.go

909 lines
29 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"database/sql"
"fmt"
"strings"
"github.com/serialx/hashring"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/pkg/util/imagetools"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type SStoragecacheManager struct {
db.SStandaloneResourceBaseManager
db.SExternalizedResourceBaseManager
SManagedResourceBaseManager
}
var StoragecacheManager *SStoragecacheManager
func init() {
StoragecacheManager = &SStoragecacheManager{
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
SStoragecache{},
"storagecaches_tbl",
"storagecache",
"storagecaches",
),
}
StoragecacheManager.SetVirtualObject(StoragecacheManager)
}
type SStoragecache struct {
db.SStandaloneResourceBase
db.SExternalizedResourceBase
SManagedResourceBase
// 镜像存储地址
Path string `width:"256" charset:"utf8" nullable:"true" list:"user" update:"admin" create:"admin_optional"` // = Column(VARCHAR(256, charset='utf8'), nullable=True)
}
func (self *SStoragecache) getStorages() []SStorage {
storages := make([]SStorage, 0)
q := StorageManager.Query().Equals("storagecache_id", self.Id)
err := db.FetchModelObjects(StorageManager, q, &storages)
if err != nil {
return nil
}
return storages
}
func (self *SStoragecache) getValidStorages() []SStorage {
storages := []SStorage{}
q := StorageManager.Query()
zones := ZoneManager.Query().Equals("status", api.ZONE_ENABLE).SubQuery()
q = q.Equals("storagecache_id", self.Id).
Filter(sqlchemy.In(q.Field("status"), []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE})).
Filter(sqlchemy.IsTrue(q.Field("enabled"))).
Filter(sqlchemy.IsFalse(q.Field("deleted")))
q = q.Join(zones, sqlchemy.Equals(q.Field("zone_id"), zones.Field("id")))
err := db.FetchModelObjects(StorageManager, q, &storages)
if err != nil {
return nil
}
return storages
}
func (self *SStoragecache) getStorageNames() []string {
storages := self.getStorages()
if storages == nil {
return nil
}
names := make([]string, len(storages))
for i := 0; i < len(storages); i += 1 {
names[i] = storages[i].Name
}
return names
}
func (self *SStoragecache) GetEsxiAgentHostDesc() (*jsonutils.JSONDict, error) {
if !strings.Contains(self.Name, "esxiagent") {
return nil, nil
}
obj, err := BaremetalagentManager.FetchById(self.ExternalId)
if err != nil {
return nil, errors.Wrapf(err, "unable to fetch baremetalagent %s", obj.GetId())
}
agent := obj.(*SBaremetalagent)
host := &SHost{}
host.Id = agent.Id
host.Name = agent.Name
host.ZoneId = agent.ZoneId
host.SetModelManager(HostManager, host)
ret := host.GetShortDesc(context.Background())
ret.Set("provider", jsonutils.NewString(api.CLOUD_PROVIDER_VMWARE))
ret.Set("brand", jsonutils.NewString(api.CLOUD_PROVIDER_VMWARE))
return ret, nil
}
func (self *SStoragecache) GetHost() (*SHost, error) {
hostId, err := self.getHostId()
if err != nil {
return nil, errors.Wrap(err, "self.getHostId")
}
if len(hostId) == 0 {
return nil, errors.Errorf("failed to get any available host for storagecache %s", self.Name)
}
host, err := HostManager.FetchById(hostId)
if err != nil {
return nil, errors.Wrap(err, "HostManager.FetchById")
}
return host.(*SHost), nil
}
func (self *SStoragecache) GetRegion() (*SCloudregion, error) {
host, err := self.GetHost()
if err != nil {
return nil, errors.Wrapf(err, "GetHost")
}
region, err := host.GetRegion()
if err != nil {
return nil, errors.Wrapf(err, "GetRegion")
}
return region, nil
}
func (self *SStoragecache) GetHosts() ([]SHost, error) {
hoststorages := HoststorageManager.Query().SubQuery()
storages := StorageManager.Query().SubQuery()
hosts := make([]SHost, 0)
host := HostManager.Query().SubQuery()
q := host.Query(host.Field("id"))
err := q.Join(hoststorages, sqlchemy.AND(
sqlchemy.Equals(hoststorages.Field("host_id"), host.Field("id")),
sqlchemy.OR(
sqlchemy.Equals(host.Field("host_status"), api.HOST_ONLINE),
sqlchemy.Equals(host.Field("host_type"), api.HOST_TYPE_BAREMETAL),
),
sqlchemy.IsTrue(host.Field("enabled")),
)).
Join(storages, sqlchemy.AND(sqlchemy.Equals(storages.Field("storagecache_id"), self.Id),
sqlchemy.In(storages.Field("status"), []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE}),
sqlchemy.IsTrue(storages.Field("enabled")))).
Filter(sqlchemy.Equals(hoststorages.Field("storage_id"), storages.Field("id"))).All(&hosts)
if err != nil {
return nil, err
}
return hosts, nil
}
func (self *SStoragecache) getHostId() (string, error) {
hosts, err := self.GetHosts()
if err != nil {
return "", errors.Wrap(err, "GetHosts")
}
hostIds := make([]string, 0)
for _, h := range hosts {
hostIds = append(hostIds, h.Id)
}
if len(hostIds) == 0 {
return "", nil
}
ring := hashring.New(hostIds)
ret, _ := ring.GetNode(self.Id)
return ret, nil
}
func (manager *SStoragecacheManager) SyncWithCloudStoragecache(ctx context.Context, userCred mcclient.TokenCredential, cloudCache cloudprovider.ICloudStoragecache, provider *SCloudprovider, xor bool) (*SStoragecache, bool, error) {
lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, userCred))
defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, userCred))
localCacheObj, err := db.FetchByExternalIdAndManagerId(manager, cloudCache.GetGlobalId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.Equals("manager_id", provider.Id)
})
if err != nil {
if err == sql.ErrNoRows {
localCache, err := manager.newFromCloudStoragecache(ctx, userCred, cloudCache, provider)
if err != nil {
return nil, false, err
} else {
return localCache, true, nil
}
} else {
return nil, false, errors.Wrapf(err, "db.FetchByExternalIdAndManagerId(%s)", cloudCache.GetGlobalId())
}
} else {
localCache := localCacheObj.(*SStoragecache)
if !xor {
localCache.syncWithCloudStoragecache(ctx, userCred, cloudCache, provider)
}
return localCache, false, nil
}
}
func (manager *SStoragecacheManager) newFromCloudStoragecache(ctx context.Context, userCred mcclient.TokenCredential, cloudCache cloudprovider.ICloudStoragecache, provider *SCloudprovider) (*SStoragecache, error) {
local := SStoragecache{}
local.SetModelManager(manager, &local)
local.ExternalId = cloudCache.GetGlobalId()
local.IsEmulated = cloudCache.IsEmulated()
local.ManagerId = provider.Id
local.Path = cloudCache.GetPath()
var err = func() error {
lockman.LockRawObject(ctx, manager.Keyword(), "name")
defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
newName, err := db.GenerateName(ctx, manager, userCred, cloudCache.GetName())
if err != nil {
return err
}
local.Name = newName
return manager.TableSpec().Insert(ctx, &local)
}()
if err != nil {
return nil, err
}
db.OpsLog.LogEvent(&local, db.ACT_CREATE, local.GetShortDesc(ctx), userCred)
return &local, nil
}
func (self *SStoragecache) syncWithCloudStoragecache(ctx context.Context, userCred mcclient.TokenCredential, cloudCache cloudprovider.ICloudStoragecache, provider *SCloudprovider) error {
diff, err := db.UpdateWithLock(ctx, self, func() error {
self.Name = cloudCache.GetName()
self.Path = cloudCache.GetPath()
self.IsEmulated = cloudCache.IsEmulated()
self.ManagerId = provider.Id
return nil
})
if err != nil {
return err
}
db.OpsLog.LogSyncUpdate(self, diff, userCred)
return nil
}
func (manager *SStoragecacheManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.StoragecacheDetails {
rows := make([]api.StoragecacheDetails, len(objs))
stdRows := manager.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
for i := range rows {
rows[i] = api.StoragecacheDetails{
StandaloneResourceDetails: stdRows[i],
ManagedResourceInfo: manRows[i],
}
rows[i] = objs[i].(*SStoragecache).getMoreDetails(ctx, rows[i])
}
return rows
}
func (self *SStoragecache) getMoreDetails(ctx context.Context, out api.StoragecacheDetails) api.StoragecacheDetails {
out.Storages = self.getStorageNames()
out.Size = self.getCachedImageSize()
out.Count = self.getCachedImageCount()
host, _ := self.GetHost()
if host != nil {
out.Host = host.GetShortDesc(ctx)
}
return out
}
func (self *SStoragecache) getCachedImageList(excludeIds []string, imageType string, status []string) []SCachedimage {
images := make([]SCachedimage, 0)
cachedImages := CachedimageManager.Query().SubQuery()
storagecachedImages := StoragecachedimageManager.Query().SubQuery()
q := cachedImages.Query()
q = q.Join(storagecachedImages, sqlchemy.Equals(cachedImages.Field("id"), storagecachedImages.Field("cachedimage_id")))
q = q.Filter(sqlchemy.Equals(storagecachedImages.Field("storagecache_id"), self.Id))
if len(excludeIds) > 0 {
q = q.Filter(sqlchemy.NotIn(cachedImages.Field("id"), excludeIds))
}
if len(imageType) > 0 {
q = q.Filter(sqlchemy.Equals(cachedImages.Field("image_type"), imageType))
}
if len(status) > 0 {
q = q.Filter(sqlchemy.In(storagecachedImages.Field("status"), status))
}
err := db.FetchModelObjects(CachedimageManager, q, &images)
if err != nil {
if err != sql.ErrNoRows {
log.Errorf("%s", err)
}
return nil
}
return images
}
func (self *SStoragecache) getCachedImages() ([]SStoragecachedimage, error) {
images := make([]SStoragecachedimage, 0)
q := StoragecachedimageManager.Query().Equals("storagecache_id", self.Id)
err := db.FetchModelObjects(StoragecachedimageManager, q, &images)
if err != nil {
return nil, errors.Wrapf(err, "db.FetchModelObjects")
}
return images, nil
}
func (self *SStoragecache) getCustomdCachedImages() ([]SStoragecachedimage, error) {
images := make([]SStoragecachedimage, 0)
sq := CachedimageManager.Query("id").Equals("image_type", "customized").SubQuery()
q := StoragecachedimageManager.Query().Equals("storagecache_id", self.Id).In("cachedimage_id", sq)
err := db.FetchModelObjects(StoragecachedimageManager, q, &images)
if err != nil {
return nil, errors.Wrapf(err, "db.FetchModelObjects")
}
return images, nil
}
func (self *SStoragecache) getCachedImageCount() int {
images, _ := self.getCachedImages()
return len(images)
}
func (self *SStoragecache) getCachedImageSize() int64 {
images, _ := self.getCachedImages()
if images == nil {
return 0
}
var size int64 = 0
for _, img := range images {
imginfo := img.GetCachedimage()
if imginfo != nil {
size += imginfo.Size
}
}
return size
}
func (self *SStoragecache) StartImageCacheTask(ctx context.Context, userCred mcclient.TokenCredential, input api.CacheImageInput) error {
StoragecachedimageManager.Register(ctx, userCred, self.Id, input.ImageId, "")
image, _ := CachedimageManager.GetImageById(ctx, userCred, input.ImageId, false)
if image != nil {
imgInfo := imagetools.NormalizeImageInfo(image.Name, image.Properties["os_arch"], image.Properties["os_type"],
image.Properties["os_distribution"], image.Properties["os_version"])
input.OsType = imgInfo.OsType
input.OsArch = imgInfo.OsArch
input.OsDistribution = imgInfo.OsDistro
input.OsVersion = imgInfo.OsVersion
input.OsFullVersion = imgInfo.OsFullVersion
input.ImageName = image.Name
}
data := jsonutils.Marshal(input).(*jsonutils.JSONDict)
task, err := taskman.TaskManager.NewTask(ctx, "StorageCacheImageTask", self, userCred, data, input.ParentTaskId, "", nil)
if err != nil {
return errors.Wrapf(err, "NewTask")
}
return task.ScheduleRun(nil)
}
func (self *SStoragecache) StartImageUncacheTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, isPurge bool, parentTaskId string) error {
data := jsonutils.NewDict()
data.Add(jsonutils.NewString(imageId), "image_id")
if isPurge {
data.Add(jsonutils.JSONTrue, "is_purge")
}
task, err := taskman.TaskManager.NewTask(ctx, "StorageUncacheImageTask", self, userCred, data, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SStoragecache) GetIStorageCache(ctx context.Context) (cloudprovider.ICloudStoragecache, error) {
storages := self.getValidStorages()
if len(storages) == 0 {
msg := fmt.Sprintf("no storages for this storagecache %s(%s)???", self.Name, self.Id)
log.Errorf(msg)
return nil, fmt.Errorf(msg)
}
istorage, err := storages[0].GetIStorage(ctx)
if err != nil {
return nil, errors.Wrapf(err, "GetIStorages")
}
return istorage.GetIStoragecache(), nil
}
// 镜像缓存存储列表
func (manager *SStoragecacheManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.StoragecacheListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StandaloneResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemFilter")
}
q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
}
q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
}
if len(query.Path) > 0 {
q = q.In("path", query.Path)
}
return q, nil
}
func (manager *SStoragecacheManager) OrderByExtraFields(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.StoragecacheListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StandaloneResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.OrderByExtraFields")
}
q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
}
return q, nil
}
func (manager *SStoragecacheManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
return q, httperrors.ErrNotFound
}
func (manager *SStoragecacheManager) FetchStoragecacheById(storageCacheId string) *SStoragecache {
iStorageCache, _ := manager.FetchById(storageCacheId)
if iStorageCache == nil {
return nil
}
return iStorageCache.(*SStoragecache)
}
func (manager *SStoragecacheManager) GetCachePathById(storageCacheId string) string {
iStorageCache, _ := manager.FetchById(storageCacheId)
if iStorageCache == nil {
return ""
}
sc := iStorageCache.(*SStoragecache)
return sc.Path
}
func (self *SStoragecache) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
if self.getCachedImageCount() > 0 {
return httperrors.NewNotEmptyError("storage cache not empty")
}
storages := self.getStorages()
if len(storages) > 0 {
return httperrors.NewNotEmptyError("referered by storages")
}
return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx, nil)
}
func (self *SStoragecache) PerformUncacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
imageStr, _ := data.GetString("image")
if len(imageStr) == 0 {
return nil, httperrors.NewMissingParameterError("image")
}
isForce := jsonutils.QueryBoolean(data, "is_force", false)
var imageId string
imgObj, err := CachedimageManager.FetchByIdOrName(nil, imageStr)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(CachedimageManager.Keyword(), imageStr)
} else {
return nil, httperrors.NewGeneralError(err)
}
} else {
cachedImage := imgObj.(*SCachedimage)
if cloudprovider.TImageType(cachedImage.ImageType) != cloudprovider.ImageTypeCustomized && !isForce {
return nil, httperrors.NewForbiddenError("cannot uncache non-customized images")
}
imageId = imgObj.GetId()
_, err := CachedimageManager.getImageInfo(ctx, userCred, imageStr, isForce)
if err != nil {
log.Infof("image %s not found %s", imageStr, err)
if !isForce {
return nil, httperrors.NewImageNotFoundError(imageStr)
}
}
}
scimg := StoragecachedimageManager.GetStoragecachedimage(self.Id, imageId)
if scimg == nil {
return nil, httperrors.NewResourceNotFoundError("storage not cache image")
}
if scimg.Status == api.CACHED_IMAGE_STATUS_INIT || isForce {
err = scimg.Detach(ctx, userCred)
return nil, err
}
err = scimg.markDeleting(ctx, userCred, isForce)
if err != nil {
return nil, httperrors.NewInvalidStatusError("Fail to mark cache status: %s", err)
}
err = self.StartImageUncacheTask(ctx, userCred, imageId, isForce, "")
return nil, err
}
func (self *SStoragecache) PerformCacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CacheImageInput) (jsonutils.JSONObject, error) {
if len(input.ImageId) == 0 {
return nil, httperrors.NewMissingParameterError("image_id")
}
image, err := CachedimageManager.getImageInfo(ctx, userCred, input.ImageId, input.IsForce)
if err != nil {
return nil, httperrors.NewImageNotFoundError(input.ImageId)
}
if len(image.Checksum) == 0 {
return nil, httperrors.NewInvalidStatusError("Cannot cache image with no checksum")
}
input.ImageId = image.Id
return nil, self.StartImageCacheTask(ctx, userCred, input)
}
func (self *SStoragecache) SyncCloudImages(
ctx context.Context,
userCred mcclient.TokenCredential,
iStoragecache cloudprovider.ICloudStoragecache,
region *SCloudregion,
xor bool,
) compare.SyncResult {
lockman.LockObject(ctx, self)
defer lockman.ReleaseObject(ctx, self)
lockman.LockRawObject(ctx, CachedimageManager.Keyword(), self.Id)
defer lockman.ReleaseRawObject(ctx, CachedimageManager.Keyword(), self.Id)
result := compare.SyncResult{}
driver, err := self.GetProviderFactory()
if err != nil {
result.Error(errors.Wrapf(err, "GetProviderFactory(%s)", region.Provider))
return result
}
if driver.IsPublicCloud() {
err = func() error {
err := region.SyncCloudImages(ctx, userCred, false, xor)
if err != nil {
return errors.Wrapf(err, "SyncCloudImages")
}
err = self.CheckCloudimages(ctx, userCred, region.Name, region.Id)
if err != nil {
return errors.Wrapf(err, "CheckCloudimages")
}
return nil
}()
if err != nil {
log.Errorf("sync public image error: %v", err)
}
log.Debugln("localCachedImages started")
localCachedImages, err := self.getCustomdCachedImages()
if err != nil {
result.Error(errors.Wrapf(err, "getCustomdCachedImages"))
return result
}
log.Debugf("localCachedImages %d", len(localCachedImages))
remoteImages, err := iStoragecache.GetICustomizedCloudImages()
if err != nil {
result.Error(errors.Wrapf(err, "GetICustomizedCloudImages"))
return result
}
result = self.syncCloudImages(ctx, userCred, localCachedImages, remoteImages, xor)
} else {
log.Debugln("localCachedImages started")
localCachedImages, err := self.getCachedImages()
if err != nil {
result.Error(errors.Wrapf(err, "getCachedImages"))
return result
}
log.Debugf("localCachedImages %d", len(localCachedImages))
remoteImages, err := iStoragecache.GetICloudImages()
if err != nil {
result.Error(errors.Wrapf(err, "GetICloudImages"))
return result
}
result = self.syncCloudImages(ctx, userCred, localCachedImages, remoteImages, xor)
}
return result
}
func (cache *SStoragecache) syncCloudImages(
ctx context.Context,
userCred mcclient.TokenCredential,
localCachedImages []SStoragecachedimage,
remoteImages []cloudprovider.ICloudImage,
xor bool,
) compare.SyncResult {
syncResult := compare.SyncResult{}
var syncOwnerId mcclient.IIdentityProvider
provider := cache.GetCloudprovider()
if provider != nil {
syncOwnerId = provider.GetOwnerId()
}
removed := make([]SStoragecachedimage, 0)
commondb := make([]SStoragecachedimage, 0)
commonext := make([]cloudprovider.ICloudImage, 0)
added := make([]cloudprovider.ICloudImage, 0)
err := compare.CompareSets(localCachedImages, remoteImages, &removed, &commondb, &commonext, &added)
if err != nil {
syncResult.Error(errors.Wrapf(err, "compare.CompareSets"))
return syncResult
}
for i := 0; i < len(removed); i += 1 {
err := removed[i].syncRemoveCloudImage(ctx, userCred)
if err != nil {
syncResult.DeleteError(err)
} else {
syncResult.Delete()
}
}
if !xor {
for i := 0; i < len(commondb); i += 1 {
err = commondb[i].syncWithCloudImage(ctx, userCred, syncOwnerId, commonext[i], cache.ManagerId)
if err != nil {
syncResult.UpdateError(err)
} else {
syncResult.Update()
}
}
}
for i := 0; i < len(added); i += 1 {
err = StoragecachedimageManager.newFromCloudImage(ctx, userCred, syncOwnerId, added[i], cache)
if err != nil {
syncResult.AddError(err)
} else {
syncResult.Add()
}
}
return syncResult
}
func (self *SStoragecache) IsReachCapacityLimit(imageId string) bool {
imgObj, _ := CachedimageManager.FetchById(imageId)
if imgObj == nil {
log.Debugf("no such cached image %s", imageId)
return false
}
cachedImage := imgObj.(*SCachedimage)
if cloudprovider.TImageType(cachedImage.ImageType) != cloudprovider.ImageTypeCustomized {
// no need to cache
log.Debugf("image %s is not a customized image, no need to cache", imageId)
return false
}
cachedImages := self.getCachedImageList(nil, string(cloudprovider.ImageTypeCustomized), []string{api.CACHED_IMAGE_STATUS_ACTIVE})
for i := range cachedImages {
if cachedImages[i].Id == imageId {
// already cached
log.Debugf("image %s has been cached in storage cache %s(%s)", imageId, self.Id, self.Name)
return false
}
}
host, _ := self.GetHost()
return host.GetHostDriver().IsReachStoragecacheCapacityLimit(host, cachedImages)
}
func (self *SStoragecache) GetStoragecachedimages() ([]SStoragecachedimage, error) {
q := StoragecachedimageManager.Query().Equals("storagecache_id", self.Id)
ret := []SStoragecachedimage{}
return ret, db.FetchModelObjects(StoragecachedimageManager, q, &ret)
}
func (self *SStoragecache) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
scis, err := self.GetStoragecachedimages()
if err != nil {
return errors.Wrapf(err, "GetStoragecachedimages")
}
for i := range scis {
err := scis[i].Delete(ctx, userCred)
if err != nil {
return errors.Wrapf(err, "delete storagecached images %d", scis[i].RowId)
}
}
if len(self.ManagerId) > 0 {
return db.RealDeleteModel(ctx, userCred, self)
}
return self.SStandaloneResourceBase.Delete(ctx, userCred)
}
func (self *SStoragecache) StartRelinquishLeastUsedCachedImageTask(ctx context.Context, userCred mcclient.TokenCredential, imageId string, parentTaskId string) error {
cachedImages := self.getCachedImageList([]string{imageId}, string(cloudprovider.ImageTypeCustomized), []string{api.CACHED_IMAGE_STATUS_ACTIVE})
leastUsedIdx := -1
leastRefCount := -1
for i := range cachedImages {
if leastRefCount < 0 || leastRefCount > cachedImages[i].RefCount {
leastRefCount = cachedImages[i].RefCount
leastUsedIdx = i
}
}
return self.StartImageUncacheTask(ctx, userCred, cachedImages[leastUsedIdx].GetId(), false, parentTaskId)
}
func (cache *SStoragecache) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
err := cache.SStandaloneResourceBase.CustomizeDelete(ctx, userCred, query, data)
if err != nil {
return err
}
if len(cache.ExternalId) > 0 {
agentObj, err := BaremetalagentManager.FetchById(cache.ExternalId)
if err == nil {
agentObj.(*SBaremetalagent).setStoragecacheId("")
} else if err != sql.ErrNoRows {
return err
}
}
return nil
}
func (manager *SStoragecacheManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
keys stringutils2.SSortedStrings,
) (*sqlchemy.SQuery, error) {
q, err := manager.SStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SStandaloneResourceBaseManager.ListItemExportKeys")
}
if keys.ContainsAny(manager.SManagedResourceBaseManager.GetExportKeys()...) {
q, err = manager.SManagedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemExportKeys")
}
}
return q, nil
}
func (self *SStoragecache) linkCloudimages(ctx context.Context, regionName, regionId string) (int, error) {
cloudimages := CloudimageManager.Query("external_id").Equals("cloudregion_id", regionId).SubQuery()
sq := StoragecachedimageManager.Query("cachedimage_id").Equals("storagecache_id", self.Id).SubQuery()
q := CachedimageManager.Query().Equals("image_type", cloudprovider.ImageTypeSystem).In("external_id", cloudimages).NotIn("id", sq)
images := []SCachedimage{}
err := db.FetchModelObjects(CachedimageManager, q, &images)
if err != nil {
return 0, errors.Wrapf(err, "db.FetchModelObjects")
}
for i := range images {
sci := &SStoragecachedimage{}
sci.SetModelManager(StoragecachedimageManager, sci)
sci.StoragecacheId = self.Id
sci.CachedimageId = images[i].Id
sci.Status = api.CACHED_IMAGE_STATUS_ACTIVE
err = StoragecachedimageManager.TableSpec().Insert(ctx, sci)
if err != nil {
return 0, errors.Wrapf(err, "Insert")
}
}
return len(images), nil
}
func (self *SStoragecache) unlinkCloudimages(ctx context.Context, userCred mcclient.TokenCredential, regionName, regionId string) (int, error) {
cloudimages := CloudimageManager.Query("external_id").Equals("cloudregion_id", regionId).SubQuery()
sq := CachedimageManager.Query("id").Equals("image_type", cloudprovider.ImageTypeSystem).NotIn("external_id", cloudimages).SubQuery()
q := StoragecachedimageManager.Query().Equals("storagecache_id", self.Id).In("cachedimage_id", sq)
scis := []SStoragecachedimage{}
err := db.FetchModelObjects(StoragecachedimageManager, q, &scis)
if err != nil {
return 0, errors.Wrapf(err, "db.FetchModelObjects")
}
for i := range scis {
err = scis[i].Delete(ctx, userCred)
if err != nil {
log.Warningf("detach image %v error: %v", scis[i].GetCachedimage(), err)
}
}
return len(scis), nil
}
func (self *SStoragecache) updateSystemImageStatus() (int, error) {
sq := CachedimageManager.Query("id").Equals("image_type", cloudprovider.ImageTypeSystem)
q := StoragecachedimageManager.Query().
Equals("storagecache_id", self.Id).In("cachedimage_id", sq.SubQuery()).
NotEquals("status", api.CACHED_IMAGE_STATUS_ACTIVE)
scis := []SStoragecachedimage{}
err := db.FetchModelObjects(StoragecachedimageManager, q, &scis)
if err != nil {
return 0, errors.Wrapf(err, "db.FetchModelObjects")
}
for i := range scis {
db.Update(&scis[i], func() error {
scis[i].Status = api.CACHED_IMAGE_STATUS_ACTIVE
return nil
})
}
return len(scis), nil
}
func (self *SStoragecache) getSystemImageCount() (int, error) {
sq := StoragecachedimageManager.Query("cachedimage_id").Equals("storagecache_id", self.Id)
q := CachedimageManager.Query().Equals("image_type", cloudprovider.ImageTypeSystem).In("id", sq.SubQuery())
return q.CountWithError()
}
func (self *SStoragecache) CheckCloudimages(ctx context.Context, userCred mcclient.TokenCredential, regionName, regionId string) error {
lockman.LockRawObject(ctx, CachedimageManager.Keyword(), regionId)
defer lockman.ReleaseRawObject(ctx, CachedimageManager.Keyword(), regionId)
result := compare.SyncResult{}
var err error
result.DelCnt, err = self.unlinkCloudimages(ctx, userCred, regionName, regionId)
if err != nil {
return errors.Wrapf(err, "unlinkCloudimages")
}
self.updateSystemImageStatus()
result.UpdateCnt, err = self.getSystemImageCount()
if err != nil {
log.Errorf("getSystemImageCount error: %v", err)
}
result.AddCnt, err = self.linkCloudimages(ctx, regionName, regionId)
if err != nil {
return errors.Wrapf(err, "linkCloudimages")
}
log.Infof("SycSystemImages for region %s(%s) storagecache %s result: %s", regionName, regionId, self.Name, result.Result())
return nil
}