| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016 |
- // 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"
- "time"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/rbacscope"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- schedapi "yunion.io/x/onecloud/pkg/apis/scheduler"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
- "yunion.io/x/onecloud/pkg/compute/options"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/util/rbacutils"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
- )
- func init() {
- InstanceSnapshotManager = &SInstanceSnapshotManager{
- SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
- SInstanceSnapshot{},
- "instance_snapshots_tbl",
- "instance_snapshot",
- "instance_snapshots",
- ),
- }
- InstanceSnapshotManager.SetVirtualObject(InstanceSnapshotManager)
- }
- type SInstanceSnapshot struct {
- db.SVirtualResourceBase
- db.SExternalizedResourceBase
- SManagedResourceBase
- SCloudregionResourceBase
- db.SMultiArchResourceBase
- db.SEncryptedResource
- // 云主机Id
- GuestId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required" index:"true"`
- // 云主机配置
- ServerConfig jsonutils.JSONObject `nullable:"true" list:"user"`
- // 云主机标签
- ServerMetadata jsonutils.JSONObject `nullable:"true" list:"user"`
- // 是否自动删除
- AutoDelete bool `default:"false" update:"user" list:"user"`
- // 引用次数
- RefCount int `default:"0" list:"user"`
- // 安全组
- SecGroups jsonutils.JSONObject `nullable:"true" list:"user"`
- // 秘钥Id
- KeypairId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
- // 操作系统类型
- OsType string `width:"36" charset:"ascii" nullable:"true" list:"user"`
- // 套餐名称
- InstanceType string `width:"64" charset:"utf8" nullable:"true" list:"user" create:"optional"`
- // 主机快照磁盘容量和
- // SizeMb int `nullable:"false" list:"user"`
- // 镜像ID
- ImageId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
- // 是否保存内存
- WithMemory bool `default:"false" get:"user" list:"user"`
- // 内存文件大小
- MemorySizeKB int `nullable:"true" get:"user" list:"user" old_name:"memory_size_mb"`
- // 内存文件所在宿主机
- MemoryFileHostId string `width:"36" charset:"ascii" nullable:"true" get:"user" list:"user"`
- // 内存文件路径
- MemoryFilePath string `width:"512" charset:"utf8" nullable:"true" get:"user" list:"user"`
- // 内存文件校验和
- MemoryFileChecksum string `width:"32" charset:"ascii" nullable:"true" get:"user" list:"user"`
- }
- type SInstanceSnapshotManager struct {
- db.SVirtualResourceBaseManager
- db.SExternalizedResourceBaseManager
- SManagedResourceBaseManager
- SCloudregionResourceBaseManager
- db.SMultiArchResourceBaseManager
- db.SEncryptedResourceManager
- }
- var InstanceSnapshotManager *SInstanceSnapshotManager
- // 主机快照列表
- func (manager *SInstanceSnapshotManager) ListItemFilter(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.InstanceSnapshotListInput,
- ) (*sqlchemy.SQuery, error) {
- q, err := manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.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")
- }
- q, err = manager.SMultiArchResourceBaseManager.ListItemFilter(ctx, q, userCred, query.MultiArchResourceBaseListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SMultiArchResourceBaseManager.ListItemFilter")
- }
- guestStr := query.ServerId
- if len(guestStr) > 0 {
- guestObj, err := GuestManager.FetchByIdOrName(ctx, userCred, guestStr)
- if err != nil {
- if errors.Cause(err) == sql.ErrNoRows {
- return nil, httperrors.NewResourceNotFoundError2("guests", guestStr)
- } else {
- return nil, httperrors.NewGeneralError(err)
- }
- }
- q = q.Equals("guest_id", guestObj.GetId())
- }
- if len(query.OsType) > 0 {
- q = q.In("os_type", query.OsType)
- }
- if query.WithMemory != nil {
- if *query.WithMemory {
- q = q.IsTrue("with_memory")
- } else {
- q = q.IsFalse("with_memory")
- }
- }
- return q, nil
- }
- func (manager *SInstanceSnapshotManager) OrderByExtraFields(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.InstanceSnapshotListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.VirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.OrderByExtraFields")
- }
- q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
- }
- if db.NeedOrderQuery([]string{query.OrderByDiskSnapshotCount}) {
- nQ := SnapshotManager.Query()
- nQ = nQ.AppendField(nQ.Field("disk_id"), sqlchemy.COUNT("snapshot_count"))
- nQ = nQ.GroupBy("disk_id")
- nSQ := nQ.SubQuery()
- guestdiskQ := GuestdiskManager.Query()
- guestdiskQ = guestdiskQ.LeftJoin(nSQ, sqlchemy.Equals(nSQ.Field("disk_id"), guestdiskQ.Field("disk_id")))
- guestdiskSQ := guestdiskQ.AppendField(guestdiskQ.Field("guest_id"), nSQ.Field("snapshot_count")).SubQuery()
- q = q.LeftJoin(guestdiskSQ, sqlchemy.Equals(guestdiskSQ.Field("guest_id"), q.Field("guest_id")))
- q = q.AppendField(q.QueryFields()...)
- q = q.AppendField(guestdiskSQ.Field("snapshot_count"))
- q = db.OrderByFields(q, []string{query.OrderByDiskSnapshotCount}, []sqlchemy.IQueryField{q.Field("snapshot_count")})
- }
- if db.NeedOrderQuery([]string{query.OrderByGuest}) {
- guestQ := GuestManager.Query()
- guestSQ := guestQ.AppendField(guestQ.Field("id"), guestQ.Field("name").Label("guest_name")).SubQuery()
- q = q.LeftJoin(guestSQ, sqlchemy.Equals(guestSQ.Field("id"), q.Field("guest_id")))
- q = q.AppendField(q.QueryFields()...)
- q = q.AppendField(guestSQ.Field("guest_name"))
- q = db.OrderByFields(q, []string{query.OrderByGuest}, []sqlchemy.IQueryField{q.Field("guest_name")})
- }
- return q, nil
- }
- func (manager *SInstanceSnapshotManager) ListItemExportKeys(ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- keys stringutils2.SSortedStrings,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, err
- }
- return q, nil
- }
- func (manager *SInstanceSnapshotManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.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 *SInstanceSnapshotManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraFields(q, resource, fields)
- if err == nil {
- return q, nil
- }
- return q, httperrors.ErrNotFound
- }
- func (self *SInstanceSnapshot) GetGuest() (*SGuest, error) {
- if len(self.GuestId) == 0 {
- return nil, errors.ErrNotFound
- }
- guest := GuestManager.FetchGuestById(self.GuestId)
- if guest == nil {
- return nil, errors.ErrNotFound
- }
- return guest, nil
- }
- func (self *SInstanceSnapshot) getMoreDetails(userCred mcclient.TokenCredential, out api.InstanceSnapshotDetails) api.InstanceSnapshotDetails {
- guest := GuestManager.FetchGuestById(self.GuestId)
- if guest != nil {
- out.Guest = guest.Name
- out.GuestStatus = guest.Status
- }
- var osType string
- provider := self.GetProviderName()
- if utils.IsInStringArray(provider, ProviderHasSubSnapshot) {
- snapshots, _ := self.GetSnapshots()
- out.Snapshots = []api.SimpleSnapshot{}
- for i := 0; i < len(snapshots); i++ {
- if snapshots[i].DiskType == api.DISK_TYPE_SYS {
- osType = snapshots[i].OsType
- }
- out.Snapshots = append(out.Snapshots, api.SimpleSnapshot{
- Id: snapshots[i].Id,
- Name: snapshots[i].Name,
- StorageId: snapshots[i].StorageId,
- DiskType: snapshots[i].DiskType,
- CloudregionId: snapshots[i].CloudregionId,
- Size: snapshots[i].Size,
- VirtualSize: snapshots[i].VirtualSize,
- Status: snapshots[i].Status,
- StorageType: snapshots[i].GetStorageType(),
- EncryptKeyId: snapshots[i].EncryptKeyId,
- CreatedAt: snapshots[i].CreatedAt,
- })
- out.SizeMb += snapshots[i].Size
- out.VirtualSizeMb += snapshots[i].VirtualSize
- if len(snapshots[i].StorageId) > 0 && out.StorageType == "" {
- out.StorageType = snapshots[i].GetStorageType()
- }
- }
- if out.VirtualSizeMb <= 0 && guest != nil {
- out.VirtualSizeMb = guest.getDiskSize()
- }
- } else if guest != nil {
- disk, err := guest.GetSystemDisk()
- if err != nil {
- log.Errorf("unable to GetSystemDisk of guest %q", guest.GetId())
- } else {
- s, _ := disk.GetStorage()
- if s != nil {
- out.StorageType = s.StorageType
- }
- }
- out.VirtualSizeMb = guest.getDiskSize()
- out.SizeMb = out.VirtualSizeMb
- }
- if len(osType) > 0 {
- out.Properties = map[string]string{"os_type": osType}
- }
- out.SizeMb += out.MemorySizeKB / 1024
- out.Size = out.SizeMb * 1024 * 1024
- return out
- }
- func (manager *SInstanceSnapshotManager) FetchCustomizeColumns(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- objs []interface{},
- fields stringutils2.SSortedStrings,
- isList bool,
- ) []api.InstanceSnapshotDetails {
- rows := make([]api.InstanceSnapshotDetails, len(objs))
- virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- encRows := manager.SEncryptedResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- for i := range rows {
- rows[i] = api.InstanceSnapshotDetails{
- VirtualResourceDetails: virtRows[i],
- ManagedResourceInfo: manRows[i],
- EncryptedResourceDetails: encRows[i],
- }
- rows[i] = objs[i].(*SInstanceSnapshot).getMoreDetails(userCred, rows[i])
- }
- return rows
- }
- func (self *SInstanceSnapshot) StartCreateInstanceSnapshotTask(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- pendingUsage quotas.IQuota,
- parentTaskId string,
- ) error {
- if task, err := taskman.TaskManager.NewTask(
- ctx, "InstanceSnapshotCreateTask", self, userCred, nil, parentTaskId, "", pendingUsage); err != nil {
- return err
- } else {
- task.ScheduleRun(nil)
- }
- return nil
- }
- func (manager *SInstanceSnapshotManager) fillInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, instanceSnapshot *SInstanceSnapshot) {
- instanceSnapshot.SetModelManager(manager, instanceSnapshot)
- instanceSnapshot.ProjectId = guest.ProjectId
- instanceSnapshot.DomainId = guest.DomainId
- instanceSnapshot.GuestId = guest.Id
- instanceSnapshot.InstanceType = guest.InstanceType
- instanceSnapshot.ImageId = guest.GetTemplateId()
- // inherit encrypt_key_id from guest
- instanceSnapshot.EncryptKeyId = guest.EncryptKeyId
- guestSchedInput := guest.ToSchedDesc()
- host, _ := guest.GetHost()
- instanceSnapshot.ManagerId = host.ManagerId
- zone, _ := host.GetZone()
- instanceSnapshot.CloudregionId = zone.CloudregionId
- for i := 0; i < len(guestSchedInput.Disks); i++ {
- guestSchedInput.Disks[i].ImageId = ""
- }
- guestSchedInput.Name = ""
- guestSchedInput.HostId = ""
- guestSchedInput.Project = ""
- guestSchedInput.Domain = ""
- for i := 0; i < len(guestSchedInput.Networks); i++ {
- guestSchedInput.Networks[i].Mac = ""
- guestSchedInput.Networks[i].Address = ""
- guestSchedInput.Networks[i].Address6 = ""
- }
- instanceSnapshot.ServerConfig = jsonutils.Marshal(guestSchedInput.ServerConfig)
- if len(guest.KeypairId) > 0 {
- keypair, _ := KeypairManager.FetchById(guest.KeypairId)
- if keypair != nil {
- instanceSnapshot.KeypairId = guest.KeypairId
- }
- }
- serverMetadata := jsonutils.NewDict()
- if loginAccount := guest.GetMetadata(ctx, "login_account", nil); len(loginAccount) > 0 {
- loginKey := guest.GetMetadata(ctx, "login_key", nil)
- if len(guest.KeypairId) == 0 && len(loginKey) > 0 {
- passwd, e := utils.DescryptAESBase64(guest.Id, loginKey)
- if e == nil {
- serverMetadata.Set("login_account", jsonutils.NewString(loginAccount))
- serverMetadata.Set("passwd", jsonutils.NewString(passwd))
- }
- } else {
- serverMetadata.Set("login_key", jsonutils.NewString(loginKey))
- serverMetadata.Set("login_account", jsonutils.NewString(loginAccount))
- }
- }
- if osArch := guest.GetMetadata(ctx, "os_arch", nil); len(osArch) > 0 {
- serverMetadata.Set("os_arch", jsonutils.NewString(osArch))
- }
- if osDist := guest.GetMetadata(ctx, "os_distribution", nil); len(osDist) > 0 {
- serverMetadata.Set("os_distribution", jsonutils.NewString(osDist))
- }
- if osName := guest.GetMetadata(ctx, "os_name", nil); len(osName) > 0 {
- serverMetadata.Set("os_name", jsonutils.NewString(osName))
- }
- if osVersion := guest.GetMetadata(ctx, "os_version", nil); len(osVersion) > 0 {
- serverMetadata.Set("os_version", jsonutils.NewString(osVersion))
- }
- secs, _ := guest.GetSecgroups()
- if len(secs) > 0 {
- secIds := make([]string, len(secs))
- for i := 0; i < len(secs); i++ {
- secIds[i] = secs[i].Id
- }
- instanceSnapshot.SecGroups = jsonutils.Marshal(secIds)
- }
- instanceSnapshot.OsType = guest.OsType
- instanceSnapshot.OsArch = guest.OsArch
- instanceSnapshot.ServerMetadata = serverMetadata
- }
- func (manager *SInstanceSnapshotManager) CreateInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, name string, autoDelete bool, withMemory bool) (*SInstanceSnapshot, error) {
- instanceSnapshot := &SInstanceSnapshot{}
- instanceSnapshot.SetModelManager(manager, instanceSnapshot)
- instanceSnapshot.Name = name
- instanceSnapshot.AutoDelete = autoDelete
- if autoDelete {
- // hide auto-delete instance snapshots
- instanceSnapshot.IsSystem = true
- }
- manager.fillInstanceSnapshot(ctx, userCred, guest, instanceSnapshot)
- // compute size of instanceSnapshot
- // instanceSnapshot.SizeMb = guest.getDiskSize()
- instanceSnapshot.WithMemory = withMemory
- instanceSnapshot.MemoryFileHostId = guest.HostId
- err := manager.TableSpec().Insert(ctx, instanceSnapshot)
- if err != nil {
- return nil, errors.Wrap(err, "Insert")
- }
- err = db.InheritFromTo(ctx, userCred, guest, instanceSnapshot)
- if err != nil {
- return nil, errors.Wrap(err, "Inherit ClassMetadata")
- }
- return instanceSnapshot, nil
- }
- var HypervisorIndependentInstanceSnapshot = []string{
- api.HYPERVISOR_KVM,
- }
- var ProviderHasSubSnapshot = []string{
- api.CLOUD_PROVIDER_ONECLOUD,
- }
- func (self *SInstanceSnapshot) ToInstanceCreateInput(
- sourceInput *api.ServerCreateInput) (*api.ServerCreateInput, error) {
- serverConfig := new(schedapi.ServerConfig)
- if err := self.ServerConfig.Unmarshal(serverConfig); err != nil {
- return nil, errors.Wrap(err, "unmarshal sched input")
- }
- provider := self.GetProviderName()
- if utils.IsInStringArray(provider, ProviderHasSubSnapshot) {
- isjs := make([]SInstanceSnapshotJoint, 0)
- err := InstanceSnapshotJointManager.Query().Equals("instance_snapshot_id", self.Id).Asc("disk_index").All(&isjs)
- if err != nil {
- return nil, errors.Wrap(err, "fetch instance snapshots")
- }
- for i := 0; i < len(serverConfig.Disks); i++ {
- index := serverConfig.Disks[i].Index
- if index < len(isjs) {
- serverConfig.Disks[i].SnapshotId = isjs[index].SnapshotId
- if i == 0 && len(self.ImageId) > 0 {
- // system disk, save ImageId
- serverConfig.Disks[i].ImageId = self.ImageId
- }
- }
- }
- }
- sourceInput.Disks = serverConfig.Disks
- if sourceInput.VmemSize == 0 {
- sourceInput.VmemSize = serverConfig.Memory
- }
- if sourceInput.VcpuCount == 0 {
- sourceInput.VcpuCount = serverConfig.Ncpu
- }
- if len(self.KeypairId) > 0 {
- sourceInput.KeypairId = self.KeypairId
- }
- if self.SecGroups != nil {
- secGroups := make([]string, 0)
- inputSecgs := make([]string, 0)
- self.SecGroups.Unmarshal(&secGroups)
- for i := 0; i < len(secGroups); i++ {
- _, err := SecurityGroupManager.FetchSecgroupById(secGroups[i])
- if err == nil {
- inputSecgs = append(inputSecgs, secGroups[i])
- }
- }
- sourceInput.Secgroups = inputSecgs
- }
- sourceInput.OsType = self.OsType
- sourceInput.OsArch = self.OsArch
- sourceInput.InstanceType = self.InstanceType
- if len(sourceInput.Networks) == 0 {
- sourceInput.Networks = serverConfig.Networks
- }
- if self.IsEncrypted() {
- if sourceInput.EncryptKeyId != nil && *sourceInput.EncryptKeyId != self.EncryptKeyId {
- return nil, errors.Wrap(httperrors.ErrConflict, "encrypt_key_id conflict with instance_snapshot's encrypt_key_id")
- }
- sourceInput.EncryptKeyId = &self.EncryptKeyId
- }
- return sourceInput, nil
- }
- func (self *SInstanceSnapshot) GetSnapshots() ([]SSnapshot, error) {
- isjq := InstanceSnapshotJointManager.Query("snapshot_id").Equals("instance_snapshot_id", self.Id)
- snapshots := make([]SSnapshot, 0)
- err := SnapshotManager.Query().In("id", isjq).All(&snapshots)
- if err != nil && err != sql.ErrNoRows {
- return nil, err
- } else if err != nil && err == sql.ErrNoRows {
- return nil, nil
- } else {
- for i := 0; i < len(snapshots); i++ {
- snapshots[i].SetModelManager(SnapshotManager, &snapshots[i])
- }
- return snapshots, nil
- }
- }
- func (self *SInstanceSnapshot) GetQuotaKeys() quotas.IQuotaKeys {
- region, _ := self.GetRegion()
- return fetchRegionalQuotaKeys(
- rbacscope.ScopeProject,
- self.GetOwnerId(),
- region,
- self.GetCloudprovider(),
- )
- }
- func (self *SInstanceSnapshot) GetUsages() []db.IUsage {
- if self.PendingDeleted || self.Deleted {
- return nil
- }
- usage := SRegionQuota{InstanceSnapshot: 1}
- keys := self.GetQuotaKeys()
- usage.SetKeys(keys)
- return []db.IUsage{
- &usage,
- }
- }
- func TotalInstanceSnapshotCount(ctx context.Context, scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObjs []db.IStandaloneModel, providers []string, brands []string, cloudEnv string, policyResult rbacutils.SPolicyResult) (int, error) {
- q := InstanceSnapshotManager.Query()
- switch scope {
- case rbacscope.ScopeSystem:
- case rbacscope.ScopeDomain:
- q = q.Equals("domain_id", ownerId.GetProjectDomainId())
- case rbacscope.ScopeProject:
- q = q.Equals("tenant_id", ownerId.GetProjectId())
- }
- q = db.ObjectIdQueryWithPolicyResult(ctx, q, InstanceSnapshotManager, policyResult)
- q = RangeObjectsFilter(q, rangeObjs, q.Field("cloudregion_id"), nil, q.Field("manager_id"), nil, nil)
- q = CloudProviderFilter(q, q.Field("manager_id"), providers, brands, cloudEnv)
- return q.CountWithError()
- }
- func (self *SInstanceSnapshot) GetInstanceSnapshotJointAt(diskIndex int) (*SInstanceSnapshotJoint, error) {
- ispj := new(SInstanceSnapshotJoint)
- err := InstanceSnapshotJointManager.Query().
- Equals("instance_snapshot_id", self.Id).Equals("disk_index", diskIndex).First(ispj)
- return ispj, err
- }
- func (self *SInstanceSnapshot) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
- if self.Status == api.INSTANCE_SNAPSHOT_START_DELETE || self.Status == api.INSTANCE_SNAPSHOT_RESET {
- return httperrors.NewForbiddenError("can't delete instance snapshot with wrong status")
- }
- return nil
- }
- func (self *SInstanceSnapshot) CustomizeDelete(
- ctx context.Context, userCred mcclient.TokenCredential,
- query jsonutils.JSONObject, data jsonutils.JSONObject) error {
- return self.StartInstanceSnapshotDeleteTask(ctx, userCred, "")
- }
- func (self *SInstanceSnapshot) StartInstanceSnapshotDeleteTask(
- ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- task, err := taskman.TaskManager.NewTask(
- ctx, "InstanceSnapshotDeleteTask", self, userCred, nil, parentTaskId, "", nil)
- if err != nil {
- log.Errorf("%s", err)
- return err
- }
- self.SetStatus(ctx, userCred, api.INSTANCE_SNAPSHOT_START_DELETE, "InstanceSnapshotDeleteTask")
- task.ScheduleRun(nil)
- return nil
- }
- func (self *SInstanceSnapshot) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- snapshots, err := self.GetSnapshots()
- if err != nil {
- return nil, err
- }
- for i := range snapshots {
- snapshotId := snapshots[i].Id
- isjp := new(SInstanceSnapshotJoint)
- err = InstanceSnapshotJointManager.Query().
- Equals("instance_snapshot_id", self.Id).Equals("snapshot_id", snapshotId).First(isjp)
- if err == nil || isjp != nil {
- isjp.SetModelManager(InstanceSnapshotJointManager, isjp)
- err = isjp.Delete(ctx, userCred)
- if err != nil {
- return nil, errors.Wrapf(err, "delete instance snapshot joint: %s", snapshotId)
- }
- } else {
- log.Errorf("failed get instance_snapshot %s join %s: %s", self.Id, snapshotId, err)
- }
- _, err = snapshots[i].PerformPurge(ctx, userCred, query, data)
- if err != nil {
- return nil, errors.Wrapf(err, "delete snapshot: %s", snapshotId)
- }
- }
- err = self.RealDelete(ctx, userCred)
- return nil, err
- }
- func (self *SInstanceSnapshot) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- return db.DeleteModel(ctx, userCred, self)
- }
- func (self *SInstanceSnapshot) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
- return nil
- }
- func (self *SInstanceSnapshot) AddRefCount(ctx context.Context) error {
- lockman.LockObject(ctx, self)
- defer lockman.ReleaseObject(ctx, self)
- _, err := db.Update(self, func() error {
- self.RefCount += 1
- return nil
- })
- return err
- }
- func (self *SInstanceSnapshot) DecRefCount(ctx context.Context, userCred mcclient.TokenCredential) error {
- lockman.LockObject(ctx, self)
- defer lockman.ReleaseObject(ctx, self)
- _, err := db.Update(self, func() error {
- self.RefCount -= 1
- return nil
- })
- if err == nil && self.RefCount == 0 && self.AutoDelete {
- self.StartInstanceSnapshotDeleteTask(ctx, userCred, "")
- }
- return err
- }
- func (is *SInstanceSnapshot) syncRemoveCloudInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential) error {
- lockman.LockObject(ctx, is)
- defer lockman.ReleaseObject(ctx, is)
- err := is.ValidateDeleteCondition(ctx, nil)
- if err != nil {
- err = is.SetStatus(ctx, userCred, api.INSTANCE_SNAPSHOT_UNKNOWN, "sync to delete")
- } else {
- err = is.RealDelete(ctx, userCred)
- }
- return err
- }
- func (is *SInstanceSnapshot) SyncWithCloudInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudInstanceSnapshot, guest *SGuest) error {
- diff, err := db.UpdateWithLock(ctx, is, func() error {
- is.Status = ext.GetStatus()
- InstanceSnapshotManager.fillInstanceSnapshot(ctx, userCred, guest, is)
- return nil
- })
- if err != nil {
- return err
- }
- db.OpsLog.LogSyncUpdate(is, diff, userCred)
- return nil
- }
- func (manager *SInstanceSnapshotManager) newFromCloudInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, extSnapshot cloudprovider.ICloudInstanceSnapshot, guest *SGuest) (*SInstanceSnapshot, error) {
- instanceSnapshot := SInstanceSnapshot{}
- instanceSnapshot.SetModelManager(manager, &instanceSnapshot)
- instanceSnapshot.ExternalId = extSnapshot.GetGlobalId()
- instanceSnapshot.Status = extSnapshot.GetStatus()
- manager.fillInstanceSnapshot(ctx, userCred, guest, &instanceSnapshot)
- var err = func() error {
- lockman.LockClass(ctx, manager, "name")
- defer lockman.ReleaseClass(ctx, manager, "name")
- newName, err := db.GenerateName(ctx, manager, nil, extSnapshot.GetName())
- if err == nil {
- instanceSnapshot.Name = extSnapshot.GetName()
- } else {
- instanceSnapshot.Name = newName
- }
- return manager.TableSpec().Insert(ctx, &instanceSnapshot)
- }()
- if err != nil {
- return nil, err
- }
- db.OpsLog.LogEvent(&instanceSnapshot, db.ACT_CREATE, instanceSnapshot.GetShortDesc(ctx), userCred)
- return &instanceSnapshot, nil
- }
- func (self *SInstanceSnapshot) GetRegionDriver() IRegionDriver {
- provider := self.GetProviderName()
- return GetRegionDriver(provider)
- }
- func (ism *SInstanceSnapshotManager) InitializeData() error {
- q := ism.Query().IsNullOrEmpty("cloudregion_id")
- var isps []SInstanceSnapshot
- err := db.FetchModelObjects(ism, q, &isps)
- if err != nil {
- return errors.Wrap(err, "unable to FetchModelObjects")
- }
- var (
- cloudregionId string
- )
- for i := range isps {
- isp := &isps[i]
- guest, err := isp.GetGuest()
- if errors.Cause(err) == errors.ErrNotFound {
- cloudregionId = api.DEFAULT_REGION_ID
- }
- if err != nil {
- return errors.Wrapf(err, "unable to GetGuest for isp %q", isp.GetId())
- } else {
- host, _ := guest.GetHost()
- zone, _ := host.GetZone()
- cloudregionId = zone.CloudregionId
- }
- _, err = db.Update(isp, func() error {
- isp.CloudregionId = cloudregionId
- return nil
- })
- if err != nil {
- return errors.Wrap(err, "unable to Update db")
- }
- }
- return nil
- }
- func (isp *SInstanceSnapshot) GetInstanceSnapshotJointsByOrder(guest *SGuest) ([]*SInstanceSnapshotJoint, error) {
- disks, err := guest.GetGuestDisks()
- if err != nil {
- return nil, errors.Wrap(err, "GetGuestDisks")
- }
- ss, err := isp.GetSnapshots()
- if err != nil {
- return nil, errors.Wrapf(err, "Get %s subsnapshots", isp.GetName())
- }
- jIsps := make([]*SInstanceSnapshotJoint, 0)
- for idx, gd := range disks {
- d := gd.GetDisk()
- if d == nil {
- return nil, errors.Wrapf(err, "Not get guestdisk %d related disk", idx)
- }
- if idx >= len(ss) {
- break
- }
- jIsp, err := isp.GetInstanceSnapshotJointAt(idx)
- if err != nil {
- return nil, errors.Wrapf(err, "GetInstanceSnapshotJointAt %d", idx)
- }
- sd, err := ss[idx].GetDisk()
- if err != nil {
- return nil, errors.Wrapf(err, "Get snapshot %d disk", idx)
- }
- if ss[idx].GetId() != jIsp.SnapshotId {
- return nil, errors.Wrapf(err, "InstanceSnapshotJoint %d snapshot_id %q != %q", idx, jIsp.SnapshotId, ss[idx].GetId())
- }
- if sd.GetId() != d.GetId() {
- return nil, errors.Wrapf(err, "Disk Snapshot %d's disk id %q != current disk %q", idx, sd.GetId(), d.GetId())
- }
- jIsps = append(jIsps, jIsp)
- }
- return jIsps, nil
- }
- func (self *SInstanceSnapshot) CustomizeCreate(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- ownerId mcclient.IIdentityProvider,
- query jsonutils.JSONObject,
- data jsonutils.JSONObject,
- ) error {
- // use disk's ownerId instead of default ownerId
- guestObj, err := GuestManager.FetchById(self.GuestId)
- if err != nil {
- return errors.Wrap(err, "GuestManager.FetchById")
- }
- ownerId = guestObj.(*SGuest).GetOwnerId()
- return self.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
- }
- func (manager *SInstanceSnapshotManager) GetNeedAutoSnapshotServers() ([]SSnapshotPolicyResource, error) {
- tz, _ := time.LoadLocation(options.Options.TimeZone)
- t := time.Now().In(tz)
- week := t.Weekday()
- if week == 0 { // sunday is zero
- week += 7
- }
- timePoint := t.Hour()
- policy := SnapshotPolicyManager.Query().Equals("type", api.SNAPSHOT_POLICY_TYPE_SERVER).Equals("cloudregion_id", api.DEFAULT_REGION_ID)
- policy = policy.Filter(sqlchemy.Contains(policy.Field("repeat_weekdays"), fmt.Sprintf("%d", week)))
- sq := policy.Filter(
- sqlchemy.OR(
- sqlchemy.Contains(policy.Field("time_points"), fmt.Sprintf(",%d,", timePoint)),
- sqlchemy.Startswith(policy.Field("time_points"), fmt.Sprintf("[%d,", timePoint)),
- sqlchemy.Endswith(policy.Field("time_points"), fmt.Sprintf(",%d]", timePoint)),
- sqlchemy.Equals(policy.Field("time_points"), fmt.Sprintf("[%d]", timePoint)),
- ),
- ).SubQuery()
- servers := GuestManager.Query().SubQuery()
- q := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_SERVER)
- q = q.Join(sq, sqlchemy.Equals(q.Field("snapshotpolicy_id"), sq.Field("id")))
- q = q.Join(servers, sqlchemy.Equals(q.Field("resource_id"), servers.Field("id")))
- ret := []SSnapshotPolicyResource{}
- err := db.FetchModelObjects(SnapshotPolicyResourceManager, q, &ret)
- if err != nil {
- return nil, err
- }
- return ret, nil
- }
- func (manager *SInstanceSnapshotManager) AutoServerSnapshot(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
- servers, err := manager.GetNeedAutoSnapshotServers()
- if err != nil {
- log.Errorf("Get auto snapshot servers id failed: %s", err)
- return
- }
- log.Infof("auto snapshot %d servers", len(servers))
- serverMap := map[string]*SGuest{}
- for i := range servers {
- server, err := servers[i].GetServer()
- if err != nil {
- log.Errorf("get server error: %v", err)
- continue
- }
- serverMap[server.Id] = server
- }
- for i := range serverMap {
- input := api.ServerInstanceSnapshot{}
- input.GenerateName = fmt.Sprintf("auto-%s-%d", serverMap[i].Name, time.Now().Unix())
- serverMap[i].PerformInstanceSnapshot(ctx, userCred, jsonutils.NewDict(), input)
- }
- }
- var instanceSnapshotCleanupTaskRunning int32 = 0
- func (manager *SInstanceSnapshotManager) CleanupInstanceSnapshots(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
- if instanceSnapshotCleanupTaskRunning > 0 {
- log.Errorf("Previous CleanupInstanceSnapshots tasks still running !!!")
- return
- }
- instanceSnapshotCleanupTaskRunning = 1
- defer func() {
- instanceSnapshotCleanupTaskRunning = 0
- }()
- sq := manager.Query().Equals("status", api.INSTANCE_SNAPSHOT_READY).Startswith("name", "auto-").SubQuery()
- iss := []struct {
- GuestCnt int
- GuestId string
- }{}
- q := sq.Query(
- sqlchemy.COUNT("guest_cnt", sq.Field("guest_id")),
- sq.Field("guest_id"),
- ).GroupBy(sq.Field("guest_id"))
- err := q.All(&iss)
- if err != nil {
- log.Errorf("Cleanup instance snapshots job fetch instance snapshot failed %s", err)
- return
- }
- guestCount := map[string]int{}
- for i := range iss {
- guestCount[iss[i].GuestId] = iss[i].GuestCnt
- }
- // cleanup retention count instance snapshots
- {
- sq = SnapshotPolicyManager.Query().Equals("type", api.SNAPSHOT_POLICY_TYPE_SERVER).GT("retention_count", 0).SubQuery()
- spr := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_SERVER).SubQuery()
- q = sq.Query(
- sq.Field("retention_count"),
- spr.Field("resource_id").Label("guest_id"),
- )
- q = q.Join(spr, sqlchemy.Equals(q.Field("id"), spr.Field("snapshotpolicy_id")))
- guestRetentions := []struct {
- GuestId string
- RetentionCount int
- }{}
- err = q.All(&guestRetentions)
- if err != nil {
- log.Errorf("Cleanup instance snapshots job fetch guest retentions failed %s", err)
- return
- }
- guestRetentionMap := map[string]int{}
- for i := range guestRetentions {
- if _, ok := guestRetentionMap[guestRetentions[i].GuestId]; !ok {
- guestRetentionMap[guestRetentions[i].GuestId] = guestRetentions[i].RetentionCount
- }
- // 取最小保留个数
- if guestRetentionMap[guestRetentions[i].GuestId] > guestRetentions[i].RetentionCount {
- guestRetentionMap[guestRetentions[i].GuestId] = guestRetentions[i].RetentionCount
- }
- }
- for guestId, retentionCnt := range guestRetentionMap {
- if cnt, ok := guestCount[guestId]; ok && cnt > retentionCnt {
- manager.startCleanupRetentionCount(ctx, userCred, guestId, cnt-retentionCnt)
- }
- }
- }
- // cleanup retention days instance snapshots
- {
- sq = SnapshotPolicyManager.Query().Equals("type", api.SNAPSHOT_POLICY_TYPE_SERVER).GT("retention_days", 0).SubQuery()
- spr := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_SERVER).SubQuery()
- q = sq.Query(
- sq.Field("retention_days"),
- spr.Field("resource_id").Label("guest_id"),
- )
- q = q.Join(spr, sqlchemy.Equals(q.Field("id"), spr.Field("snapshotpolicy_id")))
- guestRetentions := []struct {
- GuestId string
- RetentionDays int
- }{}
- err = q.All(&guestRetentions)
- if err != nil {
- log.Errorf("Cleanup instance snapshots job fetch guest retentions failed %s", err)
- return
- }
- guestRetentionMap := map[string]int{}
- for i := range guestRetentions {
- if _, ok := guestRetentionMap[guestRetentions[i].GuestId]; !ok {
- guestRetentionMap[guestRetentions[i].GuestId] = guestRetentions[i].RetentionDays
- }
- // 取最小保留天数
- if guestRetentionMap[guestRetentions[i].GuestId] > guestRetentions[i].RetentionDays {
- guestRetentionMap[guestRetentions[i].GuestId] = guestRetentions[i].RetentionDays
- }
- }
- for guestId, retentionDays := range guestRetentionMap {
- manager.startCleanupRetentionDays(ctx, userCred, guestId, retentionDays)
- }
- }
- }
- func (manager *SInstanceSnapshotManager) startCleanupRetentionCount(ctx context.Context, userCred mcclient.TokenCredential, guestId string, cnt int) error {
- q := manager.Query().Equals("guest_id", guestId).Equals("status", api.INSTANCE_SNAPSHOT_READY).Startswith("name", "auto-").Asc("created_at").Limit(cnt)
- vms := []SInstanceSnapshot{}
- err := db.FetchModelObjects(manager, q, &vms)
- if err != nil {
- return errors.Wrapf(err, "FetchModelObjects")
- }
- for i := range vms {
- vms[i].StartInstanceSnapshotDeleteTask(ctx, userCred, "")
- }
- return nil
- }
- func (manager *SInstanceSnapshotManager) startCleanupRetentionDays(ctx context.Context, userCred mcclient.TokenCredential, guestId string, day int) error {
- expiredTime := time.Now().AddDate(0, 0, -day)
- q := manager.Query().Equals("guest_id", guestId).Equals("status", api.INSTANCE_SNAPSHOT_READY).Startswith("name", "auto-").LE("created_at", expiredTime)
- vms := []SInstanceSnapshot{}
- err := db.FetchModelObjects(manager, q, &vms)
- if err != nil {
- return errors.Wrapf(err, "FetchModelObjects")
- }
- for i := range vms {
- vms[i].StartInstanceSnapshotDeleteTask(ctx, userCred, "")
- }
- return nil
- }
|