Files
cloudpods/pkg/compute/models/snapshotpolicy.go
T

420 lines
15 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"
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/pkg/utils"
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/cloudcommon/validators"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/bitmap"
)
type SSnapshotPolicyManager struct {
db.SVirtualResourceBaseManager
}
type SSnapshotPolicy struct {
db.SVirtualResourceBase
db.SExternalizedResourceBase
SManagedResourceBase
SCloudregionResourceBase
RetentionDays int `nullable:"false" list:"user" get:"user" create:"required"`
RepeatWeekdays uint8 `charset:"utf8" create:"required"`
TimePoints uint32 `charset:"utf8" create:"required"`
IsActivated bool `list:"user" get:"user" create:"optional" default:"true"`
}
var SnapshotPolicyManager *SSnapshotPolicyManager
func init() {
SnapshotPolicyManager = &SSnapshotPolicyManager{
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
SSnapshotPolicy{},
"snapshotpolicies_tbl",
"snapshotpolicy",
"snapshotpolicies",
),
}
SnapshotPolicyManager.SetVirtualObject(SnapshotPolicyManager)
}
func (manager *SSnapshotPolicyManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
input := &api.SSnapshotPolicyCreateInput{}
err := data.Unmarshal(input)
if err != nil {
return nil, httperrors.NewInputParameterError("Unmarshal input failed %s", err)
}
input.ProjectId = ownerId.GetProjectId()
input.DomainId = ownerId.GetProjectDomainId()
err = db.NewNameValidator(manager, ownerId, input.Name)
if err != nil {
return nil, err
}
managerIdV := validators.NewModelIdOrNameValidator("manager", "cloudprovider", nil)
if err := managerIdV.Validate(data); err != nil {
return nil, err
}
input.ManagerId, _ = data.GetString("manager_id")
cloudregionV := validators.NewModelIdOrNameValidator("cloudregion", "cloudregion", ownerId)
err = cloudregionV.Validate(data)
if err != nil {
return nil, err
}
cloudregion := cloudregionV.Model.(*SCloudregion)
input.CloudregionId = cloudregion.GetId()
err = cloudregion.GetDriver().ValidateCreateSnapshotPolicyData(ctx, userCred, input)
if err != nil {
return nil, err
}
internalInput := manager.sSnapshotPolicyCreateInputToInternal(input)
data = internalInput.JSON(internalInput)
return data, nil
}
func (manager *SSnapshotPolicyManager) sSnapshotPolicyCreateInputToInternal(input *api.SSnapshotPolicyCreateInput) *api.SSnapshotPolicyCreateInternalInput {
ret := api.SSnapshotPolicyCreateInternalInput{
Meta: input.Meta,
Name: input.Name,
ProjectId: input.ProjectId,
DomainId: input.DomainId,
ManagerId: input.ManagerId,
CloudregionId: input.CloudregionId,
RetentionDays: input.RetentionDays,
}
ret.RepeatWeekdays = manager.RepeatWeekdaysParseIntArray(input.RepeatWeekdays)
ret.TimePoints = manager.TimePointsParseIntArray(input.TimePoints)
return &ret
}
func (manager *SSnapshotPolicyManager) sSnapshotPolicyCreateInputFromInternal(input *api.SSnapshotPolicyCreateInternalInput) *api.SSnapshotPolicyCreateInput {
return nil
}
func (self *SSnapshotPolicy) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
self.StartCreateSnapshotPolicy(ctx, userCred, ownerId, query, data)
}
func (self *SSnapshotPolicy) StartCreateSnapshotPolicy(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
if task, err := taskman.TaskManager.NewTask(ctx, "SnapshotPolicyCreateTask", self, userCred, nil, "", "", nil); err != nil {
return err
} else {
task.ScheduleRun(nil)
}
return nil
}
func (self *SSnapshotPolicy) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
self.SetStatus(userCred, api.SNAPSHOT_POLICY_DELETING, "")
return self.StartSnapshotPolicyDeleteTask(ctx, userCred, jsonutils.NewDict(), "")
}
func (self *SSnapshotPolicy) StartSnapshotPolicyDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "SnapshotPolicyDeleteTask", self, userCred, params, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SSnapshotPolicy) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict {
ret := self.SCloudregionResourceBase.GetCustomizeColumns(ctx, userCred, query)
ret.Update(self.SVirtualResourceBase.GetCustomizeColumns(ctx, userCred, query))
// more
weekdays := SnapshotPolicyManager.RepeatWeekdaysToIntArray(self.RepeatWeekdays)
timePoints := SnapshotPolicyManager.TimePointsToIntArray(self.TimePoints)
ret.Add(jsonutils.Marshal(weekdays), "repeat_weekdays")
ret.Add(jsonutils.Marshal(timePoints), "time_points")
return ret
}
func (self *SSnapshotPolicy) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*jsonutils.JSONDict, error) {
ret := jsonutils.NewDict()
// more
weekdays := SnapshotPolicyManager.RepeatWeekdaysToIntArray(self.RepeatWeekdays)
timePoints := SnapshotPolicyManager.TimePointsToIntArray(self.TimePoints)
ret.Add(jsonutils.Marshal(weekdays), "repeat_weekdays")
ret.Add(jsonutils.Marshal(timePoints), "time_points")
return ret, nil
}
func (self *SSnapshotPolicy) GetIRegion() (cloudprovider.ICloudRegion, error) {
provider, err := self.GetDriver()
if err != nil {
return nil, fmt.Errorf("No cloudprovider for sp %s: %s", self.Name, err)
}
region := self.GetRegion()
if region == nil {
return nil, fmt.Errorf("failed to find region for sp %s", self.Name)
}
return provider.GetIRegionById(region.ExternalId)
}
func (self *SSnapshotPolicyManager) RepeatWeekdaysParseIntArray(nums []int) uint8 {
return uint8(bitmap.IntArray2Uint(nums))
}
func (self *SSnapshotPolicyManager) RepeatWeekdaysToIntArray(n uint8) []int {
return bitmap.Uint2IntArray(uint32(n))
}
func (self *SSnapshotPolicyManager) TimePointsParseIntArray(nums []int) uint32 {
return bitmap.IntArray2Uint(nums)
}
func (self *SSnapshotPolicyManager) TimePointsToIntArray(n uint32) []int {
return bitmap.Uint2IntArray(n)
}
func (self *SSnapshotPolicy) GenerateCreateSpParams() (*cloudprovider.SnapshotPolicyInput, error) {
intWeekdays := SnapshotPolicyManager.RepeatWeekdaysToIntArray(self.RepeatWeekdays)
intTimePoints := SnapshotPolicyManager.TimePointsToIntArray(self.TimePoints)
return &cloudprovider.SnapshotPolicyInput{
RetentionDays: self.RetentionDays,
RepeatWeekdays: intWeekdays,
TimePoints: intTimePoints,
PolicyName: self.Name,
}, nil
}
func (manager *SSnapshotPolicyManager) SyncSnapshotPolicies(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, region *SCloudregion, snapshots []cloudprovider.ICloudSnapshotPolicy, syncOwnerId mcclient.IIdentityProvider) compare.SyncResult {
lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId))
defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, syncOwnerId))
syncResult := compare.SyncResult{}
dbSnapshotPolicies, err := manager.getProviderSnapshotPolicies(region, provider)
if err != nil {
syncResult.Error(err)
return syncResult
}
removed := make([]SSnapshotPolicy, 0)
commondb := make([]SSnapshotPolicy, 0)
commonext := make([]cloudprovider.ICloudSnapshotPolicy, 0)
added := make([]cloudprovider.ICloudSnapshotPolicy, 0)
err = compare.CompareSets(dbSnapshotPolicies, snapshots, &removed, &commondb, &commonext, &added)
if err != nil {
syncResult.Error(err)
return syncResult
}
for i := 0; i < len(removed); i += 1 {
err = removed[i].syncRemoveCloudSnapshot(ctx, userCred)
if err != nil {
syncResult.DeleteError(err)
} else {
syncResult.Delete()
}
}
for i := 0; i < len(commondb); i += 1 {
err = commondb[i].SyncWithCloudSnapshotPolicy(ctx, userCred, commonext[i], syncOwnerId, region)
if err != nil {
syncResult.UpdateError(err)
} else {
syncMetadata(ctx, userCred, &commondb[i], commonext[i])
syncResult.Update()
}
}
for i := 0; i < len(added); i += 1 {
local, err := manager.newFromCloudSnapshotPolicy(ctx, userCred, added[i], region, syncOwnerId, provider)
if err != nil {
syncResult.AddError(err)
} else {
syncMetadata(ctx, userCred, local, added[i])
syncResult.Add()
}
}
return syncResult
}
func (manager *SSnapshotPolicyManager) getProviderSnapshotPolicies(region *SCloudregion, provider *SCloudprovider) ([]SSnapshotPolicy, error) {
if region == nil || provider == nil {
return nil, fmt.Errorf("Region is nil or provider is nil")
}
snapshotPolicies := make([]SSnapshotPolicy, 0)
q := manager.Query().Equals("cloudregion_id", region.Id).Equals("manager_id", provider.Id)
err := db.FetchModelObjects(manager, q, &snapshotPolicies)
if err != nil {
return nil, err
}
return snapshotPolicies, nil
}
func (self *SSnapshotPolicy) syncRemoveCloudSnapshot(ctx context.Context, userCred mcclient.TokenCredential) error {
lockman.LockObject(ctx, self)
defer lockman.ReleaseObject(ctx, self)
err := self.ValidateDeleteCondition(ctx)
if err != nil {
err = self.SetStatus(userCred, api.SNAPSHOT_POLICY_UNKNOWN, "sync to delete")
} else {
err = self.RealDelete(ctx, userCred)
}
return err
}
func (self *SSnapshotPolicy) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
return db.DeleteModel(ctx, userCred, self)
}
func (self *SSnapshotPolicy) SyncWithCloudSnapshotPolicy(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudSnapshotPolicy, ownerId mcclient.IIdentityProvider, region *SCloudregion) error {
diff, err := db.UpdateWithLock(ctx, self, func() error {
self.Name = ext.GetName()
self.Status = ext.GetStatus()
self.RetentionDays = ext.GetRetentionDays()
arw, err := ext.GetRepeatWeekdays()
if err != nil {
return err
}
self.RepeatWeekdays = SnapshotPolicyManager.RepeatWeekdaysParseIntArray(arw)
atp, err := ext.GetTimePoints()
if err != nil {
return err
}
self.TimePoints = SnapshotPolicyManager.TimePointsParseIntArray(atp)
return nil
})
db.OpsLog.LogSyncUpdate(self, diff, userCred)
SyncCloudProject(userCred, self, ownerId, ext, self.ManagerId)
return err
}
func (manager *SSnapshotPolicyManager) newFromCloudSnapshotPolicy(
ctx context.Context, userCred mcclient.TokenCredential,
ext cloudprovider.ICloudSnapshotPolicy, region *SCloudregion,
syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider,
) (*SSnapshotPolicy, error) {
snapshotPolicy := SSnapshotPolicy{}
snapshotPolicy.SetModelManager(manager, &snapshotPolicy)
newName, err := db.GenerateName(manager, syncOwnerId, ext.GetName())
if err != nil {
return nil, err
}
snapshotPolicy.Name = newName
snapshotPolicy.Status = ext.GetStatus()
snapshotPolicy.ExternalId = ext.GetGlobalId()
snapshotPolicy.ManagerId = provider.Id
snapshotPolicy.CloudregionId = region.Id
snapshotPolicy.RetentionDays = ext.GetRetentionDays()
arw, err := ext.GetRepeatWeekdays()
if err != nil {
return nil, err
}
snapshotPolicy.RepeatWeekdays = SnapshotPolicyManager.RepeatWeekdaysParseIntArray(arw)
atp, err := ext.GetTimePoints()
if err != nil {
return nil, err
}
snapshotPolicy.TimePoints = SnapshotPolicyManager.TimePointsParseIntArray(atp)
err = manager.TableSpec().Insert(&snapshotPolicy)
if err != nil {
log.Errorf("newFromCloudEip fail %s", err)
return nil, err
}
SyncCloudProject(userCred, &snapshotPolicy, syncOwnerId, ext, snapshotPolicy.ManagerId)
db.OpsLog.LogEvent(&snapshotPolicy, db.ACT_CREATE, snapshotPolicy.GetShortDesc(ctx), userCred)
return &snapshotPolicy, nil
}
func (self *SSnapshotPolicy) AllowPerformApplyToDisks(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "apply-to-disks")
}
func (self *SSnapshotPolicy) PerformApplyToDisks(
ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, data jsonutils.JSONObject,
) (jsonutils.JSONObject, error) {
diskIds, err := self.preCheck(ctx, userCred, query, data)
if err != nil {
return nil, err
}
return nil, self.StartApplySnapshotPolicyToDisks(ctx, userCred, diskIds)
}
func (self *SSnapshotPolicy) StartApplySnapshotPolicyToDisks(ctx context.Context, userCred mcclient.TokenCredential, diskIds []string) error {
params := jsonutils.NewDict()
params.Set("disk_ids", jsonutils.Marshal(diskIds))
if task, err := taskman.TaskManager.NewTask(ctx, "SnapshotPolicyApplyTask", self, userCred, params, "", "", nil); err != nil {
return err
} else {
task.ScheduleRun(nil)
}
return nil
}
func (self *SSnapshotPolicy) preCheck(
ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, data jsonutils.JSONObject,
) ([]string, error) {
if self.Status != api.SNAPSHOT_POLICY_READY {
return nil, httperrors.NewInvalidStatusError("Snapshot policy status %s can't do apply", self.Status)
}
jsonDiskIds, err := data.Get("disks")
if err != nil {
return nil, httperrors.NewMissingParameterError("disks")
}
ids, ok := jsonDiskIds.(*jsonutils.JSONArray)
if !ok {
return nil, httperrors.NewInputParameterError("disk_ids %s", jsonDiskIds)
}
diskIds := ids.GetStringArray()
disks := make([]string, 0)
err = DiskManager.Query("id").Equals("cloudregion_id", self.CloudregionId).
Equals("manager_id", self.ManagerId).In("id", diskIds).All(&disks)
if err != nil {
return nil, httperrors.NewInternalServerError("Query disks error %s", err)
}
if len(disks) < len(diskIds) {
notFoundDisks := make([]string, 0)
for _, id := range diskIds {
if !utils.IsInStringArray(id, disks) {
notFoundDisks = append(notFoundDisks, id)
}
}
return nil, httperrors.NewNotFoundError("Disks %v not found", notFoundDisks)
}
return diskIds, nil
}