fix(region): snapshot policy operation (#24251)

This commit is contained in:
屈轩
2026-02-05 16:40:40 +08:00
committed by GitHub
parent 43bb829edc
commit 335cdf37b4
12 changed files with 352 additions and 84 deletions

View File

@@ -42,32 +42,18 @@ import (
func init() {
cmd := shell.NewResourceCmd(&modules.Disks)
cmd.List(&compute_options.DiskListOptions{})
cmd.Show(&compute_options.DiskIdOptions{})
cmd.Perform("public", &compute_options.DiskIdOptions{})
cmd.Perform("private", &compute_options.DiskIdOptions{})
cmd.Perform("syncstatus", &compute_options.DiskIdOptions{})
cmd.Perform("change-owner-candidate-domains", &compute_options.DiskIdOptions{})
cmd.Perform("disk-cancel-delete", &compute_options.DiskIdOptions{})
cmd.Perform("set-class-metadata", &options.ResourceMetadataOptions{})
cmd.Perform("rebuild", &options.ResourceIdOptions{})
cmd.Perform("migrate", &compute_options.DiskMigrateOptions{})
cmd.Perform("change-billing-type", new(compute_options.DiskChangeBillingTypeOptions))
cmd.Perform("change-storage-type", &compute_options.DiskChangeStorageTypeOptions{})
type DiskDetailOptions struct {
ID string `help:"ID or Name of disk"`
}
R(&DiskDetailOptions{}, "disk-show", "Show details of disk", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
disk, e := modules.Disks.Get(s, args.ID, nil)
if e != nil {
return e
}
printObject(disk)
return nil
})
R(&DiskDetailOptions{}, "disk-cancel-delete", "Cancel pending delete disks", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
disk, e := modules.Disks.PerformAction(s, args.ID, "cancel-delete", nil)
if e != nil {
return e
}
printObject(disk)
return nil
})
type DiskDeleteOptions struct {
ID []string `help:"ID of disks to delete" metavar:"DISK"`
OverridePendingDelete bool `help:"Delete disk directly instead of pending delete" short-token:"f"`
@@ -96,25 +82,7 @@ func init() {
return nil
})
R(&DiskDetailOptions{}, "disk-public", "Make a disk public", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
disk, e := modules.Disks.PerformAction(s, args.ID, "public", nil)
if e != nil {
return e
}
printObject(disk)
return nil
})
R(&DiskDetailOptions{}, "disk-private", "Make a disk private", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
disk, e := modules.Disks.PerformAction(s, args.ID, "private", nil)
if e != nil {
return e
}
printObject(disk)
return nil
})
R(&DiskDetailOptions{}, "disk-metadata", "Get metadata of a disk", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
R(&compute_options.DiskIdOptions{}, "disk-metadata", "Get metadata of a disk", func(s *mcclient.ClientSession, args *compute_options.DiskIdOptions) error {
meta, e := modules.Disks.GetMetadata(s, args.ID, nil)
if e != nil {
return e
@@ -123,15 +91,6 @@ func init() {
return nil
})
R(&DiskDetailOptions{}, "disk-syncstatus", "Sync status for disk", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
ret, e := modules.Disks.PerformAction(s, args.ID, "syncstatus", nil)
if e != nil {
return e
}
printObject(ret)
return nil
})
type DiskUpdateOptions struct {
ID string `help:"ID or name of disk"`
Name string `help:"New name of disk"`
@@ -300,7 +259,7 @@ func init() {
return nil
})
R(&DiskDetailOptions{}, "disk-change-owner-candidate-domains", "Get change owner candidate domain list", func(s *mcclient.ClientSession, args *DiskDetailOptions) error {
R(&compute_options.DiskIdOptions{}, "disk-change-owner-candidate-domains", "Get change owner candidate domain list", func(s *mcclient.ClientSession, args *compute_options.DiskIdOptions) error {
result, err := modules.Disks.GetSpecific(s, args.ID, "change-owner-candidate-domains", nil)
if err != nil {
return err

View File

@@ -87,6 +87,6 @@ type CachedimageListInput struct {
// valid cachedimage
Valid bool `json:"valid"`
// enum: [public, private]
// enum: ["public", "private"]
CloudEnv string `json:"cloud_env"`
}

View File

@@ -169,6 +169,8 @@ type DiskListInput struct {
// 根据是否绑定快照策略过滤
BindingSnapshotpolicy *bool `json:"binding_snapshotpolicy"`
// 根据是否磁盘所在虚拟机是否绑定主机快照策略
BindingServerSnapshotpolicy *bool `json:"binding_server_snapshotpolicy"`
}
type DiskResourceInput struct {
@@ -213,13 +215,22 @@ type SimpleGuest struct {
Bps int `json:"bps"`
// 计费类型
BillingType string `json:"billing_type"`
// 磁盘绑定的快照策略列表
Snapshotpolicy []SimpleSnapshotPolicy `json:"snapshotpolicy"`
}
type SimpleSnapshotPolicy struct {
Id string `json:"id"`
Name string `json:"name"`
RepeatWeekdays []int `json:"repeat_weekdays"`
TimePoints []int `json:"time_points"`
// 快照策略ID
Id string `json:"id"`
// 快照策略名称
Name string `json:"name"`
// 快照策略类型
ResourceType string `json:"resource_type"`
// 快照策略重复周期
RepeatWeekdays []int `json:"repeat_weekdays"`
// 快照策略时间点
TimePoints []int `json:"time_points"`
}
type DiskDetails struct {
@@ -239,11 +250,11 @@ type DiskDetails struct {
GuestStatus string `json:"guest_status"`
// 所挂载虚拟机计费类型
GuestBillingType string `json:"guest_billing_type"`
// 磁盘所在虚拟机绑定的主机快照策略数量
GuestSnapshotpolicyCount int `json:"guest_snapshotpolicy_count"`
// 自动清理时间
AutoDeleteAt time.Time `json:"auto_delete_at"`
// 自动快照策略状态
SnapshotpolicyStatus string `json:"snapshotpolicy_status,allowempty"`
// 自动快照策略
Snapshotpolicies []SimpleSnapshotPolicy `json:"snapshotpolicies"`
@@ -351,6 +362,7 @@ type DiskChagneStorageTypeInput struct {
}
type DiskSnapshotpolicyInput struct {
// 快照策略ID
SnapshotpolicyId string `json:"snapshotpolicy_id"`
}

View File

@@ -134,6 +134,14 @@ type ServerListInput struct {
// 根据是否绑定快照策略过滤
BindingSnapshotpolicy *bool `json:"binding_snapshotpolicy"`
// 根据虚机关联的磁盘是否绑定快照策略过滤
BindingDisksSnapshotpolicy *bool `json:"binding_disks_snapshotpolicy"`
}
// 主机快照策略绑定/设置接口入参
type ServerSnapshotpolicyInput struct {
// 快照策略ID
SnapshotpolicyId string `json:"snapshotpolicy_id"`
}
func (input *ServerListInput) AfterUnmarshal() {
@@ -183,6 +191,11 @@ type ServerDetails struct {
// 磁盘概要
Disks string `json:"disks"`
// 主机快照策略数量
SnapshotpolicyCount int `json:"snapshotpolicy_count"`
// 磁盘快照策略数量
DisksSnapshotpolicyCount int `json:"disks_snapshotpolicy_count"`
// 磁盘详情
DisksInfo []GuestDiskInfo `json:"disks_info"`
// 虚拟机Ip列表

View File

@@ -218,6 +218,16 @@ func (manager *SDiskManager) ListItemFilter(
q = q.NotIn("id", spjsq)
}
}
if query.BindingServerSnapshotpolicy != nil {
guestDisks := GuestdiskManager.Query("disk_id")
sq := SnapshotPolicyResourceManager.Query("resource_id").Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_SERVER).SubQuery()
gdsq := guestDisks.Join(sq, sqlchemy.Equals(guestDisks.Field("guest_id"), sq.Field("resource_id"))).SubQuery()
if *query.BindingServerSnapshotpolicy {
q = q.In("id", gdsq)
} else {
q = q.NotIn("id", gdsq)
}
}
guestId := query.ServerId
if len(guestId) > 0 {
@@ -2550,11 +2560,12 @@ func (manager *SDiskManager) FetchCustomizeColumns(
return rows
}
guests := map[string][]api.SimpleGuest{}
guests, guestIds := map[string][]api.SimpleGuest{}, []string{}
for _, guest := range guestInfo {
_, ok := guests[guest.DiskId]
if !ok {
guests[guest.DiskId] = []api.SimpleGuest{}
guestIds = append(guestIds, guest.Id)
}
guests[guest.DiskId] = append(guests[guest.DiskId], api.SimpleGuest{
Id: guest.Id,
@@ -2571,7 +2582,7 @@ func (manager *SDiskManager) FetchCustomizeColumns(
}
policySQ := SnapshotPolicyManager.Query().SubQuery()
dps := SnapshotPolicyResourceManager.Query().Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_DISK).SubQuery()
dps := SnapshotPolicyResourceManager.Query().SubQuery()
q = policySQ.Query(
policySQ.Field("id"),
@@ -2579,8 +2590,9 @@ func (manager *SDiskManager) FetchCustomizeColumns(
policySQ.Field("time_points"),
policySQ.Field("repeat_weekdays"),
dps.Field("resource_id"),
dps.Field("resource_type"),
).Join(dps, sqlchemy.Equals(dps.Field("snapshotpolicy_id"), policySQ.Field("id"))).
Filter(sqlchemy.In(dps.Field("resource_id"), diskIds))
Filter(sqlchemy.OR(sqlchemy.In(dps.Field("resource_id"), diskIds), sqlchemy.In(dps.Field("resource_id"), guestIds)))
policyInfo := []struct {
Id string
@@ -2588,7 +2600,8 @@ func (manager *SDiskManager) FetchCustomizeColumns(
Status string
TimePoints []int
RepeatWeekdays []int
DiskId string
ResourceId string
ResourceType string
}{}
err = q.All(&policyInfo)
if err != nil {
@@ -2598,15 +2611,16 @@ func (manager *SDiskManager) FetchCustomizeColumns(
policies := map[string][]api.SimpleSnapshotPolicy{}
for _, policy := range policyInfo {
_, ok := policies[policy.DiskId]
_, ok := policies[policy.ResourceId]
if !ok {
policies[policy.DiskId] = []api.SimpleSnapshotPolicy{}
policies[policy.ResourceId] = []api.SimpleSnapshotPolicy{}
}
policies[policy.DiskId] = append(policies[policy.DiskId], api.SimpleSnapshotPolicy{
policies[policy.ResourceId] = append(policies[policy.ResourceId], api.SimpleSnapshotPolicy{
Id: policy.Id,
Name: policy.Name,
RepeatWeekdays: policy.RepeatWeekdays,
TimePoints: policy.TimePoints,
ResourceType: policy.ResourceType,
})
}
@@ -2614,12 +2628,15 @@ func (manager *SDiskManager) FetchCustomizeColumns(
rows[i].Guests, _ = guests[diskIds[i]]
names, status, billingTypes := []string{}, []string{}, []string{}
var iops, bps int
for _, guest := range rows[i].Guests {
for j := range rows[i].Guests {
guest := rows[i].Guests[j]
names = append(names, guest.Name)
status = append(status, guest.Status)
iops = guest.Iops
bps = guest.Bps
billingTypes = append(billingTypes, guest.BillingType)
rows[i].Guests[j].Snapshotpolicy, _ = policies[guest.Id]
rows[i].GuestSnapshotpolicyCount += len(rows[i].Guests[j].Snapshotpolicy)
}
rows[i].GuestCount = len(rows[i].Guests)
rows[i].Guest = strings.Join(names, ",")
@@ -3160,12 +3177,33 @@ func (disk *SDisk) GetUsages() []db.IUsage {
}
}
// 绑定磁盘快照策略
// 磁盘只能绑定一个快照策略,已绑定时报错
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
func (disk *SDisk) PerformBindSnapshotpolicy(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input *api.DiskSnapshotpolicyInput,
) (jsonutils.JSONObject, error) {
// 磁盘只能绑定一个快照策略,已绑定时报错
cnt, err := SnapshotPolicyResourceManager.GetBindingCount(disk.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
if err != nil {
return nil, errors.Wrap(err, "GetBindingCount")
}
if cnt > 0 {
return nil, httperrors.NewConflictError("disk already bound to a snapshot policy")
}
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
if guest := disk.GetGuest(); guest != nil {
guestCnt, err := SnapshotPolicyResourceManager.GetBindingCount(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
if err != nil {
return nil, errors.Wrap(err, "GetBindingCount for guest")
}
if guestCnt > 0 {
return nil, httperrors.NewConflictError("guest already has server snapshot policy, disk cannot bind snapshot policy")
}
}
spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
if err != nil {
return nil, err
@@ -3190,6 +3228,52 @@ func (disk *SDisk) PerformBindSnapshotpolicy(
return nil, sp.StartBindDisksTask(ctx, userCred, []string{disk.Id})
}
// 设置磁盘快照策略
// 可覆盖当前磁盘绑定的快照策略,若磁盘所属主机已绑定主机快照策略,则自动解除主机快照策略
func (disk *SDisk) PerformSetSnapshotpolicy(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input *api.DiskSnapshotpolicyInput,
) (jsonutils.JSONObject, error) {
spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
if err != nil {
return nil, err
}
sp := spObj.(*SSnapshotPolicy)
if sp.Type != api.SNAPSHOT_POLICY_TYPE_DISK {
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a disk snapshot policy", sp.Name)
}
if len(sp.ManagerId) > 0 {
storage, err := disk.GetStorage()
if err != nil {
return nil, errors.Wrapf(err, "GetStorage")
}
if storage.ManagerId != sp.ManagerId {
return nil, httperrors.NewConflictError("The snapshot policy %s and disk account are different", sp.Name)
}
zone, err := storage.GetZone()
if err != nil {
return nil, errors.Wrapf(err, "GetZone")
}
if sp.CloudregionId != zone.CloudregionId {
return nil, httperrors.NewConflictError("The snapshot policy %s and the disk are in different region", sp.Name)
}
}
// 先解除当前绑定再绑定新策略
if err := SnapshotPolicyResourceManager.RemoveByResource(disk.Id, api.SNAPSHOT_POLICY_TYPE_DISK); err != nil {
return nil, errors.Wrap(err, "RemoveByResource")
}
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
if guest := disk.GetGuest(); guest != nil {
if err := SnapshotPolicyResourceManager.RemoveByResource(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER); err != nil {
return nil, errors.Wrap(err, "RemoveByResource")
}
}
return nil, sp.StartBindDisksTask(ctx, userCred, []string{disk.Id})
}
// 解绑自动快照策略
func (disk *SDisk) PerformUnbindSnapshotpolicy(
ctx context.Context,
userCred mcclient.TokenCredential,

View File

@@ -5529,6 +5529,96 @@ func (self *SGuest) PerformUnbindGroups(ctx context.Context, userCred mcclient.T
return nil, nil
}
// 绑定主机快照策略
// 主机只能绑定一个快照策略,已绑定时报错
// 若主机下任意磁盘已绑定快照策略则报错
func (self *SGuest) PerformBindSnapshotpolicy(ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, input *api.ServerSnapshotpolicyInput) (jsonutils.JSONObject, error) {
if len(input.SnapshotpolicyId) == 0 {
return nil, httperrors.NewMissingParameterError("snapshotpolicy_id")
}
spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
if err != nil {
return nil, err
}
sp := spObj.(*SSnapshotPolicy)
if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
}
// 主机只能绑定一个快照策略
cnt, err := SnapshotPolicyResourceManager.GetBindingCount(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
if err != nil {
return nil, errors.Wrap(err, "GetBindingCount")
}
if cnt > 0 {
return nil, httperrors.NewConflictError("guest already bound to a snapshot policy")
}
// 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
disks, err := self.GetDisks()
if err != nil {
return nil, errors.Wrap(err, "GetDisks")
}
for _, d := range disks {
diskCnt, err := SnapshotPolicyResourceManager.GetBindingCount(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
if err != nil {
return nil, errors.Wrap(err, "GetBindingCount for disk")
}
if diskCnt > 0 {
return nil, httperrors.NewConflictError("guest has disk %s bound to snapshot policy, guest cannot bind server snapshot policy", d.Name)
}
}
sr := &SSnapshotPolicyResource{}
sr.SetModelManager(SnapshotPolicyResourceManager, sr)
sr.SnapshotpolicyId = sp.Id
sr.ResourceId = self.Id
sr.ResourceType = api.SNAPSHOT_POLICY_TYPE_SERVER
if err := SnapshotPolicyResourceManager.TableSpec().Insert(ctx, sr); err != nil {
return nil, errors.Wrap(err, "Insert")
}
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_BIND, input, userCred, true)
return nil, nil
}
// 设置主机快照策略
// 可覆盖当前主机绑定的快照策略,若主机下任意磁盘已绑定快照策略,则自动解除磁盘快照策略
func (self *SGuest) PerformSetSnapshotpolicy(ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, input *api.ServerSnapshotpolicyInput) (jsonutils.JSONObject, error) {
if len(input.SnapshotpolicyId) == 0 {
return nil, httperrors.NewMissingParameterError("snapshotpolicy_id")
}
spObj, err := validators.ValidateModel(ctx, userCred, SnapshotPolicyManager, &input.SnapshotpolicyId)
if err != nil {
return nil, err
}
sp := spObj.(*SSnapshotPolicy)
if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
}
if err := SnapshotPolicyResourceManager.RemoveByResource(self.Id, api.SNAPSHOT_POLICY_TYPE_SERVER); err != nil {
return nil, errors.Wrap(err, "RemoveByResource")
}
// 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
disks, err := self.GetDisks()
if err != nil {
return nil, errors.Wrap(err, "GetDisks")
}
for _, d := range disks {
if err := SnapshotPolicyResourceManager.RemoveByResource(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK); err != nil {
return nil, errors.Wrap(err, "RemoveByResource")
}
}
sr := &SSnapshotPolicyResource{}
sr.SetModelManager(SnapshotPolicyResourceManager, sr)
sr.SnapshotpolicyId = sp.Id
sr.ResourceId = self.Id
sr.ResourceType = api.SNAPSHOT_POLICY_TYPE_SERVER
if err := SnapshotPolicyResourceManager.TableSpec().Insert(ctx, sr); err != nil {
return nil, errors.Wrap(err, "Insert")
}
logclient.AddActionLogWithContext(ctx, self, logclient.ACT_UPDATE, input, userCred, true)
return nil, nil
}
func (self *SGuest) checkGroups(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
data jsonutils.JSONObject) (sets.String, error) {

View File

@@ -90,6 +90,15 @@ func (manager *SGuestManager) FetchCustomizeColumns(
}
}
}
if len(fields) == 0 || fields.Contains("snapshotpolicy") {
counts := fetchGuestSnapshotpolicyInfo(guestIds)
for i := range rows {
rows[i].SnapshotpolicyCount = counts[guestIds[i]]
for j := range rows[i].DisksInfo {
rows[i].DisksSnapshotpolicyCount += counts[rows[i].DisksInfo[j].Id]
}
}
}
/*if len(fields) == 0 || fields.Contains("ips") {
gips := fetchGuestIPs(guestIds, tristate.False)
if gips != nil {
@@ -440,6 +449,34 @@ func fetchGuestDisksInfo(guestIds []string) map[string][]GuestDiskInfo {
return ret
}
func fetchGuestSnapshotpolicyInfo(guestIds []string) map[string]int {
ret := map[string]int{}
disks := GuestdiskManager.Query("disk_id").In("guest_id", guestIds).SubQuery()
spq := SnapshotPolicyResourceManager.Query()
spq = spq.Filter(sqlchemy.OR(
sqlchemy.In(spq.Field("resource_id"), guestIds),
sqlchemy.In(spq.Field("resource_id"), disks),
))
sq := spq.SubQuery()
q := sq.Query(
sq.Field("resource_id"),
sqlchemy.COUNT("count", sq.Field("snapshotpolicy_id")),
).GroupBy(sq.Field("resource_id"))
counts := []struct {
ResourceId string
Count int
}{}
err := q.All(&counts)
if err != nil {
return nil
}
for _, count := range counts {
ret[count.ResourceId] = count.Count
}
return ret
}
func (guest *SGuest) GetDisksSize() int {
return guest.getDiskSize()
}

View File

@@ -738,6 +738,16 @@ func (manager *SGuestManager) ListItemFilter(
q = q.NotIn("id", spjsq)
}
}
if query.BindingDisksSnapshotpolicy != nil {
guestDisks := GuestdiskManager.Query("guest_id")
sq := SnapshotPolicyResourceManager.Query("resource_id").Equals("resource_type", api.SNAPSHOT_POLICY_TYPE_DISK).SubQuery()
gdsq := guestDisks.Join(sq, sqlchemy.Equals(guestDisks.Field("disk_id"), sq.Field("resource_id"))).SubQuery()
if *query.BindingDisksSnapshotpolicy {
q = q.In("id", gdsq)
} else {
q = q.NotIn("id", gdsq)
}
}
return q, nil
}

View File

@@ -91,3 +91,8 @@ func (man *SSnapshotPolicyResourceManager) RemoveBySnapshotpolicy(id string) err
)
return err
}
// GetBindingCount returns the number of snapshot policies bound to the given resource.
func (man *SSnapshotPolicyResourceManager) GetBindingCount(resourceId, resourceType string) (int, error) {
return man.Query().Equals("resource_id", resourceId).Equals("resource_type", resourceType).CountWithError()
}

View File

@@ -510,6 +510,12 @@ func (sp *SSnapshotPolicy) PerformBindDisks(
return nil, sp.StartBindDisksTask(ctx, userCred, diskIds)
}
// 绑定资源
// 目前仅支持绑定主机和磁盘
// 磁盘只能绑定一个快照策略,已绑定时报错
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定快照策略
// 主机只能绑定一个快照策略,已绑定时报错
// 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
func (sp *SSnapshotPolicy) PerformBindResources(
ctx context.Context,
userCred mcclient.TokenCredential,
@@ -522,15 +528,63 @@ func (sp *SSnapshotPolicy) PerformBindResources(
for i := range input.Resources {
switch input.Resources[i].Type {
case api.SNAPSHOT_POLICY_TYPE_DISK:
_, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.Resources[i].Id)
if sp.Type != api.SNAPSHOT_POLICY_TYPE_DISK {
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a disk snapshot policy", sp.Name)
}
diskObj, err := validators.ValidateModel(ctx, userCred, DiskManager, &input.Resources[i].Id)
if err != nil {
return nil, err
}
disk := diskObj.(*SDisk)
// 磁盘只能绑定一个快照策略
cnt, err := SnapshotPolicyResourceManager.GetBindingCount(disk.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
if err != nil {
return nil, errors.Wrap(err, "GetBindingCount")
}
if cnt > 0 {
return nil, httperrors.NewConflictError("disk %s already bound to a snapshot policy", disk.Name)
}
// 若磁盘所属主机已绑定主机快照策略,则磁盘不能再绑定
if guest := disk.GetGuest(); guest != nil {
guestCnt, err := SnapshotPolicyResourceManager.GetBindingCount(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
if err != nil {
return nil, errors.Wrap(err, "GetBindingCount for guest")
}
if guestCnt > 0 {
return nil, httperrors.NewConflictError("guest %s already has server snapshot policy, disk cannot bind snapshot policy", guest.Name)
}
}
case api.SNAPSHOT_POLICY_TYPE_SERVER:
_, err := validators.ValidateModel(ctx, userCred, GuestManager, &input.Resources[i].Id)
if sp.Type != api.SNAPSHOT_POLICY_TYPE_SERVER {
return nil, httperrors.NewBadRequestError("The snapshot policy %s is not a server snapshot policy", sp.Name)
}
guestObj, err := validators.ValidateModel(ctx, userCred, GuestManager, &input.Resources[i].Id)
if err != nil {
return nil, err
}
guest := guestObj.(*SGuest)
// 主机只能绑定一个快照策略
cnt, err := SnapshotPolicyResourceManager.GetBindingCount(guest.Id, api.SNAPSHOT_POLICY_TYPE_SERVER)
if err != nil {
return nil, errors.Wrap(err, "GetBindingCount")
}
if cnt > 0 {
return nil, httperrors.NewConflictError("guest %s already bound to a snapshot policy", guest.Name)
}
// 若主机下任意磁盘已绑定快照策略,则主机不能再绑定主机快照策略
disks, err := guest.GetDisks()
if err != nil {
return nil, errors.Wrap(err, "guest.GetDisks")
}
for _, d := range disks {
diskCnt, err := SnapshotPolicyResourceManager.GetBindingCount(d.Id, api.SNAPSHOT_POLICY_TYPE_DISK)
if err != nil {
return nil, errors.Wrap(err, "GetBindingCount for disk")
}
if diskCnt > 0 {
return nil, httperrors.NewConflictError("guest %s has disk %s bound to snapshot policy, guest cannot bind server snapshot policy", guest.Name, d.Name)
}
}
default:
return nil, httperrors.NewBadRequestError("Invalid resource type: %s", input.Resources[i].Type)
}

View File

@@ -78,30 +78,34 @@ func (o DiskCreateOptions) Params() (*api.DiskCreateInput, error) {
return params, nil
}
type DiskMigrateOptions struct {
ID string `help:"ID of the server" json:"-"`
TargetStorageId string `help:"Disk migrate target storage id or name" json:"target_storage_id"`
type DiskIdOptions struct {
ID string `help:"ID or Name of disk"`
}
func (o *DiskMigrateOptions) GetId() string {
func (o *DiskIdOptions) GetId() string {
return o.ID
}
func (o *DiskIdOptions) Params() (jsonutils.JSONObject, error) {
return nil, nil
}
type DiskMigrateOptions struct {
DiskIdOptions
TargetStorageId string `help:"Disk migrate target storage id or name" json:"target_storage_id"`
}
func (o *DiskMigrateOptions) Params() (jsonutils.JSONObject, error) {
return options.StructToParams(o)
}
type DiskChangeStorageTypeOptions struct {
ID string `help:"ID of the server" json:"-"`
DiskIdOptions
StorageType string `help:"Disk migrate target storage type" json:"storage_type"`
}
func (o *DiskChangeStorageTypeOptions) GetId() string {
return o.ID
}
func (o *DiskChangeStorageTypeOptions) Params() (jsonutils.JSONObject, error) {
return options.StructToParams(o)
}
@@ -124,7 +128,9 @@ type DiskListOptions struct {
SnapshotpolicyId string `help:"snapshotpolicy id"`
StorageHostId string `help:"filter disk by host"`
StorageHostId string `help:"filter disk by host"`
BindingServerSnapshotpolicy *bool `help:"filter disk by binding server snapshotpolicy" negative:"no-binding-server-snapshotpolicy"`
BindingSnapshotpolicy *bool `help:"filter disk by binding snapshotpolicy" negative:"no-binding-snapshotpolicy"`
}
func (opts *DiskListOptions) Params() (jsonutils.JSONObject, error) {
@@ -143,14 +149,10 @@ func (opts *DiskListOptions) Params() (jsonutils.JSONObject, error) {
}
type DiskChangeBillingTypeOptions struct {
ID string
DiskIdOptions
BillingType string `choices:"prepaid|postpaid"`
}
func (o *DiskChangeBillingTypeOptions) GetId() string {
return o.ID
}
func (o *DiskChangeBillingTypeOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(map[string]string{"billing_type": o.BillingType}), nil
}

View File

@@ -79,8 +79,10 @@ type ServerListOptions struct {
WithUserMeta *bool `help:"filter by user metadata" negative:"without_user_meta"`
WithHost *bool `help:"filter guest with host or not" negative:"without_host"`
SnapshotpolicyId string `help:"filter guest with snapshotpolicy or not" json:"snapshotpolicy_id"`
WithHost *bool `help:"filter guest with host or not" negative:"without_host"`
SnapshotpolicyId string `help:"filter guest with snapshotpolicy or not" json:"snapshotpolicy_id"`
BindingDisksSnapshotpolicy *bool `help:"filter guest with disks binding snapshotpolicy or not" negative:"no-binding-disks-snapshotpolicy" json:"binding_disks_snapshotpolicy"`
BindingSnapshotpolicy *bool `help:"filter guest with binding snapshotpolicy or not" negative:"no-binding-snapshotpolicy" json:"binding_snapshotpolicy"`
}
func (o *ServerListOptions) Params() (jsonutils.JSONObject, error) {