| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136 |
- // 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"
- "math/rand"
- "strings"
- "time"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/gotypes"
- "yunion.io/x/pkg/tristate"
- "yunion.io/x/pkg/util/httputils"
- "yunion.io/x/pkg/util/rbacscope"
- "yunion.io/x/pkg/util/timeutils"
- "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/compute/options"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/mcclient/modules/image"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
- )
- type SCachedimageManager struct {
- db.SSharableVirtualResourceBaseManager
- db.SExternalizedResourceBaseManager
- }
- var CachedimageManager *SCachedimageManager
- func init() {
- CachedimageManager = &SCachedimageManager{
- SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
- SCachedimage{},
- "cachedimages_tbl",
- "cachedimage",
- "cachedimages",
- ),
- }
- CachedimageManager.SetVirtualObject(CachedimageManager)
- CachedimageManager.TableSpec().AddIndex(false, "deleted", "domain_id", "tenant_id", "image_type")
- }
- type SCachedimage struct {
- db.SSharableVirtualResourceBase
- db.SExternalizedResourceBase
- // 镜像大小单位: Byte
- // example: 53687091200
- Size int64 `nullable:"false" list:"user" update:"admin" create:"admin_required"`
- // 镜像详情信息
- // example: {"deleted":false,"disk_format":"qcow2","id":"img-a6uucnfl","is_public":true,"min_disk":51200,"min_ram":0,"name":"FreeBSD 11.1 64bit","properties":{"os_arch":"x86_64","os_distribution":"FreeBSD","os_type":"FreeBSD","os_version":"11"},"protected":true,"size":53687091200,"status":"active"}
- Info jsonutils.JSONObject `nullable:"true" list:"user" update:"admin" create:"admin_required"`
- // 上此同步时间
- // example: 2020-01-17T05:28:54.000000Z
- LastSync time.Time `list:"admin"`
- // 最近一次缓存引用时间
- // 2020-01-17T05:20:54.000000Z
- LastRef time.Time `list:"admin"`
- // 引用次数
- // example: 0
- RefCount int `default:"0" list:"user"`
- // 是否支持UEFI
- // example: false
- UEFI tristate.TriState `default:"false" list:"user"`
- // 镜像类型, system: 公有云镜像, customized: 自定义镜像
- // example: system
- ImageType string `width:"16" default:"customized" list:"user" index:"true"`
- }
- func (manager *SCachedimageManager) GetResourceCount() ([]db.SScopeResourceCount, error) {
- return []db.SScopeResourceCount{}, nil
- }
- func (cachedImage SCachedimage) GetGlobalId() string {
- return cachedImage.ExternalId
- }
- func (cachedImage *SCachedimage) ValidateDeleteCondition(ctx context.Context, info *api.CachedimageDetails) error {
- if gotypes.IsNil(info) {
- info = &api.CachedimageDetails{}
- count, err := CachedimageManager.TotalResourceCount([]string{cachedImage.Id})
- if err != nil {
- return err
- }
- info.CachedimageUsage, _ = count[cachedImage.Id]
- }
- if info.CachedCount > 0 {
- return httperrors.NewNotEmptyError("The image has been cached on storages")
- }
- if cachedImage.GetStatus() == api.CACHED_IMAGE_STATUS_ACTIVE && !cachedImage.isReferenceSessionExpire() {
- return httperrors.NewConflictError("the image reference session has not been expired!")
- }
- return cachedImage.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx, nil)
- }
- func (cachedImage *SCachedimage) isReferenceSessionExpire() bool {
- if !cachedImage.LastRef.IsZero() && time.Now().Sub(cachedImage.LastRef) < api.CACHED_IMAGE_REFERENCE_SESSION_EXPIRE_SECONDS*time.Second {
- return false
- } else {
- return true
- }
- }
- func (cachedImage *SCachedimage) isRefreshSessionExpire() bool {
- if len(cachedImage.ExternalId) > 0 { // external image info never expires
- return false
- }
- if !cachedImage.LastRef.IsZero() && time.Now().Sub(cachedImage.LastRef) < api.CACHED_IMAGE_REFRESH_SECONDS*time.Second {
- return false
- } else {
- return true
- }
- }
- func (cachedImage *SCachedimage) GetHosts() ([]SHost, error) {
- q := HostManager.Query().Distinct()
- hs := HoststorageManager.Query().SubQuery()
- storages := StorageManager.Query().SubQuery()
- scis := StoragecachedimageManager.Query().Equals("cachedimage_id", cachedImage.Id).Equals("status", api.CACHED_IMAGE_STATUS_ACTIVE).SubQuery()
- q = q.Join(hs, sqlchemy.Equals(hs.Field("host_id"), q.Field("id")))
- q = q.Join(storages, sqlchemy.Equals(hs.Field("storage_id"), storages.Field("id")))
- q = q.Join(scis, sqlchemy.Equals(storages.Field("storagecache_id"), scis.Field("storagecache_id")))
- ret := []SHost{}
- err := db.FetchModelObjects(HostManager, q, &ret)
- if err != nil {
- return nil, err
- }
- return ret, err
- }
- func (cachedImage *SCachedimage) GetName() string {
- name, _ := cachedImage.Info.GetString("name")
- return name
- }
- func (cachedImage *SCachedimage) GetOwner() string {
- owner, _ := cachedImage.Info.GetString("owner")
- return owner
- }
- func (cachedImage *SCachedimage) GetFormat() string {
- format, _ := cachedImage.Info.GetString("disk_format")
- return format
- }
- func (cachedImage *SCachedimage) GetStatus() string {
- status, _ := cachedImage.Info.GetString("status")
- return status
- }
- func (cachedImage *SCachedimage) GetOSType() string {
- osType, _ := cachedImage.Info.GetString("properties", "os_type")
- return osType
- }
- func (cachedImage *SCachedimage) GetOSDistribution() string {
- osType, _ := cachedImage.Info.GetString("properties", "os_distribution")
- return osType
- }
- func (cachedImage *SCachedimage) GetOSVersion() string {
- osType, _ := cachedImage.Info.GetString("properties", "os_version")
- return osType
- }
- func (cachedImage *SCachedimage) GetHypervisor() string {
- osType, _ := cachedImage.Info.GetString("properties", "hypervisor")
- return osType
- }
- func (cachedImage *SCachedimage) GetChecksum() string {
- checksum, _ := cachedImage.Info.GetString("checksum")
- return checksum
- }
- func (cachedImage *SCachedimage) getStoragecacheQuery() *sqlchemy.SQuery {
- q := StoragecachedimageManager.Query().Equals("cachedimage_id", cachedImage.Id)
- return q
- }
- func (cachedImage *SCachedimage) getStoragecacheCount() (int, error) {
- return cachedImage.getStoragecacheQuery().CountWithError()
- }
- func (cachedImage *SCachedimage) GetImage() (*cloudprovider.SImage, error) {
- image := cloudprovider.SImage{
- ExternalId: cachedImage.ExternalId,
- }
- err := cachedImage.Info.Unmarshal(&image)
- if err != nil {
- log.Errorf("unmarshal %s fail %s", cachedImage.Info, err)
- return nil, errors.Wrap(err, "cachedImage.Info.Unmarshal")
- } else {
- // hack, make cached image ID consistent
- image.Id = cachedImage.Id
- return &image, nil
- }
- }
- func (cachedImage *SCachedimage) syncClassMetadata(ctx context.Context, userCred mcclient.TokenCredential) error {
- session := auth.GetSessionWithInternal(ctx, userCred, "")
- ret, err := image.Images.GetSpecific(session, cachedImage.Id, "class-metadata", nil)
- if err != nil {
- return errors.Wrap(err, "unable to get class_metadata")
- }
- classMetadata := make(map[string]string, 0)
- err = ret.Unmarshal(&classMetadata)
- if err != nil {
- return err
- }
- return cachedImage.SetClassMetadataAll(ctx, classMetadata, userCred)
- }
- func (manager *SCachedimageManager) cacheGlanceImageInfo(ctx context.Context, userCred mcclient.TokenCredential, info jsonutils.JSONObject) (*SCachedimage, error) {
- lockman.LockRawObject(ctx, manager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
- img := struct {
- Id string
- Size int64
- Name string
- Status string
- IsPublic bool
- ProjectId string `json:"tenant_id"`
- DomainId string
- PublicScope string
- }{}
- err := info.Unmarshal(&img)
- if err != nil {
- return nil, errors.Wrapf(err, "Unmarshal %s", info.String())
- }
- if len(img.Id) == 0 {
- return nil, fmt.Errorf("invalid image info")
- }
- if len(img.Name) == 0 {
- img.Name = img.Id
- }
- imageCache := SCachedimage{}
- imageCache.SetModelManager(manager, &imageCache)
- err = manager.RawQuery().Equals("id", img.Id).First(&imageCache)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows { // insert
- imageCache.Id = img.Id
- imageCache.Name = img.Name
- imageCache.Size = img.Size
- imageCache.Info = info
- imageCache.Status = img.Status
- imageCache.IsPublic = img.IsPublic
- imageCache.PublicScope = img.PublicScope
- imageCache.ProjectId = img.ProjectId
- imageCache.DomainId = img.DomainId
- imageCache.LastSync = timeutils.UtcNow()
- err = manager.TableSpec().Insert(ctx, &imageCache)
- if err != nil {
- return nil, err
- }
- db.OpsLog.LogEvent(&imageCache, db.ACT_CREATE, info, userCred)
- return &imageCache, nil
- } else {
- log.Errorf("fetching image cache (%s) failed: %s", img.Id, err)
- return nil, err
- }
- } else { // update
- diff, err := db.Update(&imageCache, func() error {
- imageCache.Size = img.Size
- imageCache.Info = info
- imageCache.Status = img.Status
- imageCache.IsPublic = img.IsPublic
- imageCache.PublicScope = img.PublicScope
- imageCache.LastSync = timeutils.UtcNow()
- imageCache.ProjectId = img.ProjectId
- imageCache.DomainId = img.DomainId
- if imageCache.Deleted {
- imageCache.Deleted = false
- imageCache.DeletedAt = time.Time{}
- imageCache.RefCount = 0
- imageCache.UpdateVersion = 0
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- db.OpsLog.LogEvent(&imageCache, db.ACT_UPDATE, diff, userCred)
- return &imageCache, nil
- }
- }
- func (manager *SCachedimageManager) RecoverCachedImage(ctx context.Context, userCred mcclient.TokenCredential, imgId string) (*SCachedimage, error) {
- lockman.LockRawObject(ctx, manager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
- imageCache := SCachedimage{}
- imageCache.SetModelManager(manager, &imageCache)
- err := manager.RawQuery().Equals("id", imgId).First(&imageCache)
- if err != nil {
- return nil, err
- }
- diff, err := db.Update(&imageCache, func() error {
- imageCache.Status = api.CACHED_IMAGE_STATUS_ACTIVE
- imageCache.LastSync = timeutils.UtcNow()
- if imageCache.Deleted {
- imageCache.Deleted = false
- imageCache.DeletedAt = time.Time{}
- imageCache.RefCount = 0
- imageCache.UpdateVersion = 0
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- db.OpsLog.LogEvent(&imageCache, db.ACT_UPDATE, diff, userCred)
- return &imageCache, nil
- }
- func (image *SCachedimage) GetStorages() ([]SStorage, error) {
- sq := StorageManager.Query()
- storagecacheimageSubq := StoragecachedimageManager.Query("storagecache_id").Equals("cachedimage_id", image.GetId()).SubQuery()
- sq.Join(storagecacheimageSubq, sqlchemy.Equals(sq.Field("storagecache_id"), storagecacheimageSubq.Field("storagecache_id")))
- storages := make([]SStorage, 0, 1)
- err := db.FetchModelObjects(StorageManager, sq, &storages)
- if err != nil {
- return nil, errors.Wrap(err, "FetchModelObjects")
- }
- return storages, nil
- }
- func (manager *SCachedimageManager) GetCachedimageById(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*SCachedimage, error) {
- img, err := manager.FetchById(imageId)
- if err == nil {
- cachedImage := img.(*SCachedimage)
- oSTypeOk := options.Options.NoCheckOsTypeForCachedImage || len(cachedImage.GetOSType()) > 0
- if (!refresh && cachedImage.GetStatus() == cloudprovider.IMAGE_STATUS_ACTIVE && oSTypeOk && !cachedImage.isRefreshSessionExpire()) || options.Options.ProhibitRefreshingCloudImage || len(cachedImage.ExternalId) > 0 {
- return cachedImage, nil
- }
- }
- if err != nil && errors.Cause(err) != sql.ErrNoRows {
- return nil, err
- }
- s := auth.GetAdminSession(ctx, options.Options.Region)
- obj, err := image.Images.Get(s, imageId, nil)
- if err != nil {
- log.Errorf("GetImageById %s error %s", imageId, err)
- return nil, errors.Wrap(err, "modules.Images.Get")
- }
- cachedImage, err := manager.cacheGlanceImageInfo(ctx, userCred, obj)
- if err != nil {
- return nil, errors.Wrap(err, "manager.cacheGlanceImageInfo")
- }
- return cachedImage, nil
- }
- func (manager *SCachedimageManager) GetImageById(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*cloudprovider.SImage, error) {
- imgObj, _ := manager.FetchById(imageId)
- if imgObj != nil {
- cachedImage := imgObj.(*SCachedimage)
- oSTypeOk := options.Options.NoCheckOsTypeForCachedImage || len(cachedImage.GetOSType()) > 0
- if !refresh && cachedImage.GetStatus() == cloudprovider.IMAGE_STATUS_ACTIVE && oSTypeOk && !cachedImage.isRefreshSessionExpire() {
- return cachedImage.GetImage()
- } else if options.Options.ProhibitRefreshingCloudImage {
- return cachedImage.GetImage()
- } else if len(cachedImage.ExternalId) > 0 { // external image, request refresh
- return cachedImage.requestRefreshExternalImage(ctx, userCred)
- }
- }
- s := auth.GetAdminSession(ctx, options.Options.Region)
- obj, err := image.Images.Get(s, imageId, nil)
- if err != nil {
- return nil, errors.Wrap(err, "modules.Images.Get")
- }
- cachedImage, err := manager.cacheGlanceImageInfo(ctx, userCred, obj)
- if err != nil {
- return nil, errors.Wrap(err, "manager.cacheGlanceImageInfo")
- }
- return cachedImage.GetImage()
- }
- func (manager *SCachedimageManager) getImageByName(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*cloudprovider.SImage, error) {
- imgObj, _ := manager.FetchByName(ctx, userCred, imageId)
- if imgObj != nil {
- cachedImage := imgObj.(*SCachedimage)
- if !refresh && cachedImage.GetStatus() == cloudprovider.IMAGE_STATUS_ACTIVE && len(cachedImage.GetOSType()) > 0 && !cachedImage.isRefreshSessionExpire() {
- return cachedImage.GetImage()
- } else if options.Options.ProhibitRefreshingCloudImage {
- return cachedImage.GetImage()
- } else if len(cachedImage.ExternalId) > 0 { // external image, request refresh
- return cachedImage.requestRefreshExternalImage(ctx, userCred)
- }
- }
- s := auth.GetSession(ctx, userCred, options.Options.Region)
- obj, err := image.Images.GetByName(s, imageId, nil)
- if err != nil {
- if httputils.ErrorCode(err) == 404 {
- err = httperrors.ErrNotFound
- }
- return nil, errors.Wrap(err, "modules.Images.GetByName")
- }
- cachedImage, err := manager.cacheGlanceImageInfo(ctx, userCred, obj)
- if err != nil {
- return nil, errors.Wrap(err, "manager.cacheGlanceImageInfo")
- }
- return cachedImage.GetImage()
- }
- func (manager *SCachedimageManager) GetImageInfo(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*cloudprovider.SImage, error) {
- return manager.getImageInfo(ctx, userCred, imageId, refresh)
- }
- func (manager *SCachedimageManager) getImageInfo(ctx context.Context, userCred mcclient.TokenCredential, imageId string, refresh bool) (*cloudprovider.SImage, error) {
- img, err := manager.GetImageById(ctx, userCred, imageId, refresh)
- if err == nil {
- return img, nil
- }
- log.Errorf("getImageInfoById %s fail %s", imageId, err)
- return manager.getImageByName(ctx, userCred, imageId, refresh)
- }
- func (cm *SCachedimageManager) query(manager db.IModelManager, field string, cacheIds []string, filter func(*sqlchemy.SQuery) *sqlchemy.SQuery) *sqlchemy.SSubQuery {
- q := manager.Query()
- if filter != nil {
- q = filter(q)
- }
- sq := q.SubQuery()
- return sq.Query(
- sq.Field("cachedimage_id"),
- sqlchemy.COUNT(field),
- ).In("cachedimage_id", cacheIds).GroupBy(sq.Field("cachedimage_id")).SubQuery()
- }
- type CachedimageUsageCount struct {
- Id string
- api.CachedimageUsage
- }
- func (manager *SCachedimageManager) TotalResourceCount(cacheIds []string) (map[string]api.CachedimageUsage, error) {
- ret := map[string]api.CachedimageUsage{}
- scSQ := manager.query(StoragecachedimageManager, "cached_cnt", cacheIds, nil)
- caches := manager.Query().SubQuery()
- cachesQ := caches.Query(
- sqlchemy.SUM("cached_count", scSQ.Field("cached_cnt")),
- )
- cachesQ.AppendField(cachesQ.Field("id"))
- cachesQ = cachesQ.LeftJoin(scSQ, sqlchemy.Equals(cachesQ.Field("id"), scSQ.Field("cachedimage_id")))
- cachesQ = cachesQ.Filter(sqlchemy.In(cachesQ.Field("id"), cacheIds)).GroupBy(cachesQ.Field("id"))
- counts := []CachedimageUsageCount{}
- err := cachesQ.All(&counts)
- if err != nil {
- return nil, errors.Wrapf(err, "cachesQ.All")
- }
- for i := range counts {
- ret[counts[i].Id] = counts[i].CachedimageUsage
- }
- return ret, nil
- }
- func (manager *SCachedimageManager) FetchCustomizeColumns(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- objs []interface{},
- fields stringutils2.SSortedStrings,
- isList bool,
- ) []api.CachedimageDetails {
- rows := make([]api.CachedimageDetails, len(objs))
- virtRows := manager.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- cacheIds := make([]string, len(objs))
- for i := range rows {
- ci := objs[i].(*SCachedimage)
- rows[i] = api.CachedimageDetails{
- SharableVirtualResourceDetails: virtRows[i],
- OsType: ci.GetOSType(),
- OsDistribution: ci.GetOSDistribution(),
- OsVersion: ci.GetOSVersion(),
- Hypervisor: ci.GetHypervisor(),
- }
- cacheIds[i] = ci.Id
- }
- usage, err := manager.TotalResourceCount(cacheIds)
- if err != nil {
- log.Errorf("TotalResourceCount error: %v", err)
- return rows
- }
- for i := range rows {
- rows[i].CachedimageUsage, _ = usage[cacheIds[i]]
- }
- return rows
- }
- func (cachedImage *SCachedimage) PerformRefresh(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- img, err := CachedimageManager.GetImageById(ctx, userCred, cachedImage.Id, true)
- if err != nil {
- return nil, err
- }
- return jsonutils.Marshal(img), nil
- }
- // 清除镜像缓存
- func (cachedImage *SCachedimage) PerformUncacheImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CachedImageUncacheImageInput) (jsonutils.JSONObject, error) {
- if len(input.StoragecacheId) == 0 {
- return nil, httperrors.NewMissingParameterError("storagecache_id")
- }
- _storagecache, err := StoragecacheManager.FetchById(input.StoragecacheId)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError("failed to found storagecache %s", input.StoragecacheId)
- }
- return nil, errors.Wrap(err, "StoragecacheManager.FetchById")
- }
- storagecache := _storagecache.(*SStoragecache)
- return storagecache.PerformUncacheImage(ctx, userCred, query, jsonutils.Marshal(map[string]interface{}{"image": cachedImage.Id, "is_force": input.IsForce}))
- }
- func (manager *SCachedimageManager) PerformCacheImage(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.CachedImageManagerCacheImageInput,
- ) (jsonutils.JSONObject, error) {
- if len(input.ImageId) == 0 {
- return nil, httperrors.NewMissingParameterError("image_id")
- }
- s := auth.GetAdminSession(ctx, options.Options.Region)
- obj, err := image.Images.Get(s, input.ImageId, nil)
- if err != nil {
- return nil, errors.Wrap(err, "modules.Images.Get")
- }
- cimg, err := manager.cacheGlanceImageInfo(ctx, userCred, obj)
- if err != nil {
- return nil, errors.Wrap(err, "manager.cacheGlanceImageInfo")
- }
- if input.AutoCache {
- storageCaches, err := StoragecacheManager.FetchStoragecachesByFilters(ctx, input.SStorageCacheFilters)
- if err != nil {
- return nil, errors.Wrap(err, "FetchStoragecachesByFilters")
- }
- err = StoragecacheManager.StartImageCacheTask(ctx, userCred, storageCaches, api.CacheImageInput{
- ImageId: cimg.Id,
- PreCache: true,
- })
- if err != nil {
- return nil, errors.Wrap(err, "StoragecacheManager.StartImageCacheTask")
- }
- }
- return nil, nil
- }
- func (cachedImage *SCachedimage) addRefCount() {
- if cachedImage.GetStatus() != api.CACHED_IMAGE_STATUS_ACTIVE {
- return
- }
- _, err := db.Update(cachedImage, func() error {
- cachedImage.RefCount += 1
- cachedImage.LastRef = timeutils.UtcNow()
- return nil
- })
- if err != nil {
- log.Errorf("addRefCount fail %s", err)
- }
- }
- func (cachedImage *SCachedimage) ChooseSourceStoragecacheInRange(hostType string, excludes []string, rangeObjs []interface{}) (*SStoragecachedimage, error) {
- storageCachedImage := StoragecachedimageManager.Query().SubQuery()
- storage := StorageManager.Query().SubQuery()
- hostStorage := HoststorageManager.Query().SubQuery()
- host := HostManager.Query().SubQuery()
- scimgs := make([]SStoragecachedimage, 0)
- q := storageCachedImage.Query().
- Join(storage, sqlchemy.Equals(storage.Field("storagecache_id"), storageCachedImage.Field("storagecache_id"))).
- Join(hostStorage, sqlchemy.Equals(hostStorage.Field("storage_id"), storage.Field("id"))).
- Join(host, sqlchemy.Equals(hostStorage.Field("host_id"), host.Field("id"))).
- Filter(sqlchemy.Equals(storageCachedImage.Field("cachedimage_id"), cachedImage.Id)).
- Filter(sqlchemy.Equals(storageCachedImage.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE)).
- Filter(sqlchemy.Equals(host.Field("status"), api.HOST_STATUS_RUNNING)).
- Filter(sqlchemy.IsTrue(host.Field("enabled"))).
- Filter(sqlchemy.Equals(host.Field("host_status"), api.HOST_ONLINE)).
- Filter(sqlchemy.IsTrue(storage.Field("enabled"))).
- Filter(sqlchemy.In(storage.Field("status"), []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE})).
- Filter(sqlchemy.Equals(storage.Field("storage_type"), api.STORAGE_LOCAL))
- if len(excludes) > 0 {
- q = q.Filter(sqlchemy.NotIn(host.Field("id"), excludes))
- }
- if len(hostType) > 0 {
- q = q.Filter(sqlchemy.Equals(host.Field("host_type"), hostType))
- }
- for _, rangeObj := range rangeObjs {
- switch v := rangeObj.(type) {
- case *SHost:
- q = q.Filter(sqlchemy.Equals(host.Field("id"), v.Id))
- case *SZone:
- q = q.Filter(sqlchemy.Equals(host.Field("zone_id"), v.Id))
- case *SCloudprovider:
- q = q.Filter(sqlchemy.Equals(host.Field("manager_id"), v.Id))
- }
- }
- err := db.FetchModelObjects(StoragecachedimageManager, q, &scimgs)
- if err != nil {
- return nil, err
- }
- if len(scimgs) == 0 {
- return nil, nil
- }
- return &scimgs[rand.Intn(len(scimgs))], nil
- }
- func (manager *SCachedimageManager) ImageAddRefCount(imageId string) {
- cachedObj, _ := manager.FetchById(imageId)
- if cachedObj != nil {
- cachedImage := (cachedObj).(*SCachedimage)
- cachedImage.addRefCount()
- }
- }
- func (cachedImage *SCachedimage) canDeleteLastCache() bool {
- cnt, err := cachedImage.getStoragecacheCount()
- if err != nil {
- // database error
- return false
- }
- if cnt != 1 {
- return true
- }
- if cachedImage.isReferenceSessionExpire() {
- return true
- }
- return false
- }
- func (cachedImage *SCachedimage) syncWithCloudImage(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, image cloudprovider.ICloudImage, provider *SCloudprovider) error {
- diff, err := db.UpdateWithLock(ctx, cachedImage, func() error {
- if options.Options.EnableSyncName {
- newName, err := db.GenerateAlterName(cachedImage, image.GetName())
- if err != nil {
- return errors.Wrap(err, "GenerateAlterName")
- }
- cachedImage.Name = newName
- }
- cachedImage.Size = image.GetSizeByte()
- cachedImage.ExternalId = image.GetGlobalId()
- cachedImage.ImageType = string(image.GetImageType())
- cachedImage.PublicScope = string(image.GetPublicScope())
- cachedImage.Status = image.GetStatus()
- if image.GetPublicScope() == rbacscope.ScopeSystem {
- cachedImage.IsPublic = true
- }
- cachedImage.UEFI = tristate.NewFromBool(cloudprovider.IsUEFI(image))
- sImage := cloudprovider.CloudImage2Image(image)
- cachedImage.Info = jsonutils.Marshal(&sImage)
- cachedImage.LastSync = time.Now().UTC()
- return nil
- })
- db.OpsLog.LogSyncUpdate(cachedImage, diff, userCred)
- if provider != nil {
- SyncCloudProject(ctx, userCred, cachedImage, ownerId, image, provider)
- }
- return err
- }
- func (manager *SCachedimageManager) newFromCloudImage(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, image cloudprovider.ICloudImage, provider *SCloudprovider) (*SCachedimage, error) {
- cachedImage := SCachedimage{}
- cachedImage.SetModelManager(manager, &cachedImage)
- cachedImage.Size = image.GetSizeByte()
- cachedImage.UEFI = tristate.NewFromBool(cloudprovider.IsUEFI(image))
- sImage := cloudprovider.CloudImage2Image(image)
- cachedImage.Info = jsonutils.Marshal(&sImage)
- cachedImage.LastSync = time.Now().UTC()
- cachedImage.ImageType = string(image.GetImageType())
- cachedImage.ExternalId = image.GetGlobalId()
- cachedImage.Status = image.GetStatus()
- cachedImage.ProjectId = ownerId.GetProjectId()
- cachedImage.DomainId = ownerId.GetProjectDomainId()
- cachedImage.PublicScope = string(image.GetPublicScope())
- switch image.GetPublicScope() {
- case rbacscope.ScopeNone:
- default:
- cachedImage.IsPublic = true
- }
- var err error
- err = func() error {
- lockman.LockRawObject(ctx, manager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
- cachedImage.Name, err = db.GenerateName(ctx, manager, nil, image.GetName())
- if err != nil {
- return err
- }
- return manager.TableSpec().Insert(ctx, &cachedImage)
- }()
- if err != nil {
- return nil, err
- }
- if provider != nil {
- SyncCloudProject(ctx, userCred, &cachedImage, ownerId, image, provider)
- }
- return &cachedImage, nil
- }
- func (image *SCachedimage) requestRefreshExternalImage(ctx context.Context, userCred mcclient.TokenCredential) (*cloudprovider.SImage, error) {
- caches := image.getValidStoragecache()
- if len(caches) == 0 {
- log.Errorf("requestRefreshExternalImage: no valid storage cache")
- return nil, fmt.Errorf("no valid storage cache")
- }
- var cache *SStoragecache
- var cachedImage *SStoragecachedimage
- for i := range caches {
- ci := StoragecachedimageManager.GetStoragecachedimage(caches[i].Id, image.Id)
- if ci != nil {
- cache = &caches[i]
- cachedImage = ci
- break
- }
- }
- if cache == nil {
- return nil, fmt.Errorf("no cached image found")
- }
- iCache, err := cache.GetIStorageCache(ctx)
- if err != nil {
- log.Errorf("GetIStorageCache fail %s", err)
- return nil, err
- }
- iImage, err := iCache.GetIImageById(cachedImage.ExternalId)
- if err != nil {
- log.Errorf("iCache.GetIImageById fail %s", err)
- return nil, err
- }
- err = image.syncWithCloudImage(ctx, userCred, nil, iImage, nil)
- if err != nil {
- log.Errorf("image.syncWithCloudImage fail %s", err)
- return nil, err
- }
- return image.GetImage()
- }
- func (image *SCachedimage) getValidStoragecache() []SStoragecache {
- storagecaches := StoragecacheManager.Query().SubQuery()
- storagecacheimages := StoragecachedimageManager.Query().SubQuery()
- providers := usableCloudProviders().SubQuery()
- q := storagecaches.Query()
- q = q.Join(providers, sqlchemy.Equals(providers.Field("id"), storagecaches.Field("manager_id")))
- q = q.Join(storagecacheimages, sqlchemy.Equals(storagecaches.Field("id"), storagecacheimages.Field("storagecache_id")))
- q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("cachedimage_id"), image.Id))
- q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE))
- caches := make([]SStoragecache, 0)
- err := db.FetchModelObjects(StoragecacheManager, q, &caches)
- if err != nil {
- log.Errorf("getValidStoragecache fail %s", err)
- return nil
- }
- return caches
- }
- func (image *SCachedimage) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
- db.SharedResourceManager.CleanModelShares(ctx, userCred, image.GetISharableVirtualModel())
- return db.RealDeleteModel(ctx, userCred, image)
- }
- func (image *SCachedimage) GetRegions() ([]SCloudregion, error) {
- regions := []SCloudregion{}
- caches := image.getValidStoragecache()
- for _, cache := range caches {
- region, err := cache.GetRegion()
- if err != nil {
- return nil, err
- }
- regions = append(regions, *region)
- }
- return regions, nil
- }
- func (image *SCachedimage) GetUsableZoneIds() ([]string, error) {
- zones := ZoneManager.Query().SubQuery()
- storages := StorageManager.Query().SubQuery()
- storagecaches := StoragecacheManager.Query().SubQuery()
- storagecacheimages := StoragecachedimageManager.Query().SubQuery()
- providers := usableCloudProviders().SubQuery()
- q := zones.Query(zones.Field("id"))
- q = q.Join(storages, sqlchemy.Equals(q.Field("id"), storages.Field("zone_id")))
- q = q.Join(storagecaches, sqlchemy.Equals(storages.Field("storagecache_id"), storagecaches.Field("id")))
- q = q.Join(providers, sqlchemy.Equals(providers.Field("id"), storagecaches.Field("manager_id")))
- q = q.Join(storagecacheimages, sqlchemy.Equals(storagecaches.Field("id"), storagecacheimages.Field("storagecache_id")))
- q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("cachedimage_id"), image.Id))
- q = q.Filter(sqlchemy.Equals(storagecacheimages.Field("status"), api.CACHED_IMAGE_STATUS_ACTIVE))
- q = q.Filter(sqlchemy.Equals(q.Field("status"), api.ZONE_ENABLE))
- result := []string{}
- rows, err := q.Rows()
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, nil
- }
- return nil, err
- }
- defer rows.Close()
- for rows.Next() {
- var zoneId string
- if err := rows.Scan(&zoneId); err != nil {
- return nil, err
- }
- result = append(result, zoneId)
- }
- return result, nil
- }
- func (image *SCachedimage) GetCloudprovider() (*SCloudprovider, error) {
- caches := image.getValidStoragecache()
- if len(caches) == 0 {
- return nil, fmt.Errorf("no valid storagecache for image %s(%s)", image.Name, image.Id)
- }
- cloudprovider := caches[0].GetCloudprovider()
- if cloudprovider == nil {
- return nil, fmt.Errorf("failed to found cloudprovider for storagecache %s(%s)", caches[0].Name, caches[0].Id)
- }
- return cloudprovider, nil
- }
- // 缓存镜像列表
- func (manager *SCachedimageManager) ListItemFilter(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.CachedimageListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SSharableBaseResourceManager.ListItemFilter(ctx, q, userCred, query.SharableResourceBaseListInput)
- if err != nil {
- return nil, errors.Wrapf(err, "SSharableBaseResourceManager.ListItemFilter")
- }
- q, err = manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.SharableVirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
- }
- {
- storagecachedImages := StoragecachedimageManager.Query("cachedimage_id").Equals("status", api.CACHED_IMAGE_STATUS_ACTIVE) // .SubQuery()
- storagecachedImages = storagecachedImages.Snapshot()
- storagesQ := StorageManager.Query("storagecache_id")
- storagesQ = storagesQ.Snapshot()
- if query.Valid {
- storagesQ = storagesQ.In("status", []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE}).IsTrue("enabled")
- }
- if len(query.CloudproviderId) > 0 {
- storagesQ = storagesQ.In("manager_id", query.CloudproviderId)
- }
- if len(query.CloudEnv) > 0 {
- switch query.CloudEnv {
- case api.CLOUD_ENV_PUBLIC_CLOUD:
- pubQ := CloudproviderManager.GetPublicProviderIdsQuery()
- storagesQ = storagesQ.In("manager_id", pubQ)
- case api.CLOUD_ENV_PRIVATE_CLOUD:
- privQ := CloudproviderManager.GetPrivateProviderIdsQuery()
- storagesQ = storagesQ.In("manager_id", privQ)
- case api.CLOUD_ENV_ON_PREMISE:
- storagesQ = storagesQ.IsNullOrEmpty("manager_id")
- case api.CLOUD_ENV_PRIVATE_ON_PREMISE:
- privQ := CloudproviderManager.GetPrivateProviderIdsQuery()
- storagesQ = storagesQ.Filter(
- sqlchemy.OR(
- sqlchemy.In(storagesQ.Field("manager_id"), privQ),
- sqlchemy.IsNullOrEmpty(storagesQ.Field("manager_id")),
- ),
- )
- }
- }
- if len(query.HostSchedtagId) > 0 {
- hostschedtags := HostschedtagManager.Query("host_id").Equals("schedtag_id", query.HostSchedtagId)
- hoststorages := HoststorageManager.Query("storage_id").In("host_id", hostschedtags.Distinct().SubQuery())
- storagesQ = storagesQ.In("id", hoststorages.Distinct().SubQuery())
- }
- zonesQ := ZoneManager.Query("id")
- zonesQ = zonesQ.Snapshot()
- if len(query.ZoneId) > 0 {
- zonesQ = zonesQ.Equals("id", query.ZoneId)
- }
- if len(query.CloudregionId) > 0 {
- zonesQ = zonesQ.In("cloudregion_id", query.CloudregionId)
- }
- if zonesQ.IsAltered() {
- storagesQ = storagesQ.Filter(
- sqlchemy.In(storagesQ.Field("zone_id"), zonesQ.Distinct().SubQuery()),
- )
- }
- if storagesQ.IsAltered() {
- storagecachedImages = storagecachedImages.Filter(
- sqlchemy.In(storagecachedImages.Field("storagecache_id"), storagesQ.Distinct().SubQuery()),
- )
- }
- if storagecachedImages.IsAltered() {
- q = q.In("id", storagecachedImages.Distinct().SubQuery())
- }
- }
- if len(query.ImageType) > 0 {
- q = q.Equals("image_type", query.ImageType)
- }
- return q, nil
- }
- func (manager *SCachedimageManager) OrderByExtraFields(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.CachedimageListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SSharableVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.SharableVirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.OrderByExtraFields")
- }
- return q, nil
- }
- func (manager *SCachedimageManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SSharableVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- return q, httperrors.ErrNotFound
- }
- func (manager *SCachedimageManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
- q, err := manager.SSharableVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrapf(err, "SSharableVirtualResourceBaseManager.L.ListItemExportKeys")
- }
- return q, nil
- }
- // 清理已经删除的镜像缓存
- func (manager *SCachedimageManager) AutoCleanImageCaches(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
- defer func() {
- err := manager.cleanExternalImages()
- if err != nil {
- log.Errorf("cleanExternalImages error: %v", err)
- }
- err = manager.cleanStoragecachedimages()
- if err != nil {
- log.Errorf("cleanStoragecachedimages error: %v", err)
- }
- }()
- lastSync := time.Now().Add(time.Duration(-1*api.CACHED_IMAGE_REFERENCE_SESSION_EXPIRE_SECONDS) * time.Second)
- q := manager.Query()
- q = q.LT("last_sync", lastSync).Equals("status", api.CACHED_IMAGE_STATUS_ACTIVE).IsNullOrEmpty("external_id").Limit(50)
- caches := []SCachedimage{}
- err := db.FetchModelObjects(manager, q, &caches)
- if err != nil {
- return
- }
- s := auth.GetAdminSession(ctx, options.Options.Region)
- for i := range caches {
- _, err := image.Images.Get(s, caches[i].Id, nil)
- if err != nil {
- if e, ok := err.(*httputils.JSONClientError); ok && e.Code == 404 {
- e := caches[i].ValidateDeleteCondition(ctx, nil)
- if e == nil {
- caches[i].Delete(ctx, userCred)
- continue
- }
- deleteMark := "-deleted@"
- db.Update(&caches[i], func() error {
- if !strings.Contains(caches[i].Name, deleteMark) {
- caches[i].Name = fmt.Sprintf("%s%s%s", caches[i].Name, deleteMark, timeutils.ShortDate(time.Now()))
- }
- return nil
- })
- }
- continue
- }
- db.Update(&caches[i], func() error {
- caches[i].LastSync = time.Now()
- return nil
- })
- }
- }
- func (manager *SCachedimageManager) getExpireExternalImageIds() ([]string, error) {
- ids := []string{}
- templatedIds := DiskManager.Query("template_id").IsNotEmpty("template_id").Distinct().SubQuery()
- cachedimageIds := StoragecachedimageManager.Query("cachedimage_id").Distinct().SubQuery()
- externalIds := CloudimageManager.Query("external_id").Distinct().SubQuery()
- q := manager.RawQuery("id")
- q = q.Filter(
- sqlchemy.AND(
- sqlchemy.IsNotEmpty(q.Field("external_id")),
- sqlchemy.NotIn(q.Field("id"), templatedIds),
- sqlchemy.NotIn(q.Field("id"), cachedimageIds),
- sqlchemy.NotIn(q.Field("external_id"), externalIds),
- ),
- )
- rows, err := q.Rows()
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return ids, nil
- }
- return nil, errors.Wrap(err, "Query")
- }
- defer rows.Close()
- for rows.Next() {
- var id string
- err := rows.Scan(&id)
- if err != nil {
- return nil, errors.Wrapf(err, "rows.Scan")
- }
- ids = append(ids, id)
- }
- return ids, nil
- }
- func (manager *SCachedimageManager) cleanExternalImages() error {
- ids, err := manager.getExpireExternalImageIds()
- if err != nil {
- return errors.Wrapf(err, "getExpireExternalImageIds")
- }
- err = db.Purge(manager, "id", ids, true)
- if err != nil {
- return errors.Wrapf(err, "purge")
- }
- log.Debugf("clean %d expired external images", len(ids))
- return nil
- }
- func (manager *SCachedimageManager) cleanStoragecachedimages() error {
- ids, err := db.FetchField(StoragecachedimageManager, "row_id", func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- sq := manager.Query("id").Distinct().SubQuery()
- return q.NotIn("cachedimage_id", sq)
- })
- if err != nil {
- return errors.Wrapf(err, "getExpireExternalImageIds")
- }
- err = db.Purge(StoragecachedimageManager, "row_id", ids, true)
- if err != nil {
- return errors.Wrapf(err, "purge")
- }
- log.Debugf("clean %d invalid storagecachedimages", len(ids))
- return nil
- }
- func (image *SCachedimage) GetAllClassMetadata() (map[string]string, error) {
- meta, err := image.SSharableVirtualResourceBase.GetAllClassMetadata()
- if err != nil {
- return nil, errors.Wrap(err, "SSharableVirtualResourceBase.GetAllClassMetadata")
- }
- metaDict, _ := image.Info.GetMap("metadata")
- for k, v := range metaDict {
- if !strings.HasPrefix(k, db.CLASS_TAG_PREFIX) {
- continue
- }
- meta[k[len(db.CLASS_TAG_PREFIX):]], _ = v.GetString()
- }
- return meta, nil
- }
|