diff --git a/cmd/climc/shell/misc/current.go b/cmd/climc/shell/misc/current.go index c015bef198..90349a5bea 100644 --- a/cmd/climc/shell/misc/current.go +++ b/cmd/climc/shell/misc/current.go @@ -15,7 +15,10 @@ package misc import ( + "yunion.io/x/pkg/errors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/modulebase" ) func init() { @@ -25,4 +28,36 @@ func init() { printObject(s.ToJson()) return nil }) + type FreezeResrouceOptions struct { + ID string `help:"ID of resource"` + Module string `help:"Resource module type, eg: servers, disks, networks"` + } + R(&FreezeResrouceOptions{}, "freeze-resource", "Freeze resource operation update and perform action except for unfreeze", func(s *mcclient.ClientSession, args *FreezeResrouceOptions) error { + mod, err := modulebase.GetModule(s, args.Module) + if err != nil { + return errors.Wrap(err, "failed get module") + } + obj, err := mod.PerformAction(s, args.ID, "freeze", nil) + if err != nil { + return err + } + printObject(obj) + return nil + }) + type UnfreezeResrouceOptions struct { + ID string `help:"ID of resource"` + Module string `help:"Resource module type, eg: servers, disks, networks"` + } + R(&UnfreezeResrouceOptions{}, "unfreeze-resource", "Unfreeze resource", func(s *mcclient.ClientSession, args *UnfreezeResrouceOptions) error { + mod, err := modulebase.GetModule(s, args.Module) + if err != nil { + return errors.Wrap(err, "failed get module") + } + obj, err := mod.PerformAction(s, args.ID, "unfreeze", nil) + if err != nil { + return err + } + printObject(obj) + return nil + }) } diff --git a/pkg/apis/input.go b/pkg/apis/input.go index e048d18c42..3cf87d0c33 100644 --- a/pkg/apis/input.go +++ b/pkg/apis/input.go @@ -241,6 +241,12 @@ type PerformChangeProjectOwnerInput struct { ProjectizedResourceInput } +type PerformFreezeInput struct { +} + +type PerformUnfreezeInput struct { +} + type PerformChangeDomainOwnerInput struct { DomainizedResourceInput } diff --git a/pkg/cloudcommon/db/opslog_const.go b/pkg/cloudcommon/db/opslog_const.go index 2bb7a7f7a3..b77ec507a6 100644 --- a/pkg/cloudcommon/db/opslog_const.go +++ b/pkg/cloudcommon/db/opslog_const.go @@ -47,6 +47,9 @@ const ( ACT_BACKUP_START = "backup_start" ACT_BACKUP_START_FAILED = "backup_start_fail" + ACT_FREEZE = "freeze" + ACT_UNFREEZE = "unfreeze" + ACT_RESTARING = "restarting" ACT_RESTART_FAIL = "restart_fail" diff --git a/pkg/cloudcommon/db/virtualresource.go b/pkg/cloudcommon/db/virtualresource.go index a0bdba2921..ae0463ac4b 100644 --- a/pkg/cloudcommon/db/virtualresource.go +++ b/pkg/cloudcommon/db/virtualresource.go @@ -64,6 +64,8 @@ type SVirtualResourceBase struct { PendingDeletedAt time.Time `json:"pending_deleted_at" list:"user"` // 资源是否处于回收站中 PendingDeleted bool `nullable:"false" default:"false" index:"true" get:"user" list:"user" json:"pending_deleted"` + + Freezed bool `nullable:"false" default:"false" get:"user" list:"user" json:"freezed"` } func (model *SVirtualResourceBase) IsOwner(userCred mcclient.TokenCredential) bool { @@ -228,8 +230,21 @@ func (manager *SVirtualResourceBaseManager) FetchCustomizeColumns( return ret } +func (model *SVirtualResourceBase) PreCheckPerformAction( + ctx context.Context, userCred mcclient.TokenCredential, + action string, query jsonutils.JSONObject, data jsonutils.JSONObject, +) error { + if err := model.SStandaloneResourceBase.PreCheckPerformAction(ctx, userCred, action, query, data); err != nil { + return err + } + if model.Freezed && action != "unfreeze" { + return httperrors.NewBadRequestError("Virtual resource freezed, can't do %s", action) + } + return nil +} + func (model *SVirtualResourceBase) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool { - return model.IsOwner(userCred) || IsAdminAllowUpdate(userCred, model) + return !model.Freezed && (model.IsOwner(userCred) || IsAdminAllowUpdate(userCred, model)) } func (model *SVirtualResourceBase) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { @@ -253,6 +268,47 @@ func (model *SVirtualResourceBase) GetTenantCache(ctx context.Context) (*STenant return TenantCacheManager.FetchTenantById(ctx, model.ProjectId) } +func (model *SVirtualResourceBase) AllowPerformFreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformFreezeInput) bool { + return IsAdminAllowPerform(userCred, model, "freeze") +} + +// freezed update and perform action operation except for unfreeze +func (model *SVirtualResourceBase) PerformFreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformFreezeInput) (jsonutils.JSONObject, error) { + if model.Freezed { + return nil, httperrors.NewBadRequestError("virtual resource already freezed") + } + _, err := Update(model, func() error { + model.Freezed = true + return nil + }) + if err != nil { + return nil, err + } + OpsLog.LogEvent(model, ACT_FREEZE, "perform freeze", userCred) + logclient.AddActionLogWithContext(ctx, model, logclient.ACT_FREEZE, "perform freeze", userCred, true) + return nil, nil +} + +func (model *SVirtualResourceBase) AllowPerformUnfreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformUnfreezeInput) bool { + return IsAdminAllowPerform(userCred, model, "unfreeze") +} + +func (model *SVirtualResourceBase) PerformUnfreeze(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformUnfreezeInput) (jsonutils.JSONObject, error) { + if !model.Freezed { + return nil, httperrors.NewBadRequestError("virtual resource not freezed") + } + _, err := Update(model, func() error { + model.Freezed = false + return nil + }) + if err != nil { + return nil, err + } + OpsLog.LogEvent(model, ACT_UNFREEZE, "perform unfreeze", userCred) + logclient.AddActionLogWithContext(ctx, model, logclient.ACT_UNFREEZE, "perform unfreeze", userCred, true) + return nil, nil +} + func (model *SVirtualResourceBase) AllowPerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) bool { return IsAdminAllowPerform(userCred, model, "change-owner") } diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index c7377fa805..70c68471d7 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -89,6 +89,9 @@ func (self *SGuest) PreCheckPerformAction( ctx context.Context, userCred mcclient.TokenCredential, action string, query jsonutils.JSONObject, data jsonutils.JSONObject, ) error { + if err := self.SVirtualResourceBase.PreCheckPerformAction(ctx, userCred, action, query, data); err != nil { + return err + } if self.Hypervisor == api.HYPERVISOR_KVM { host := self.GetHost() if host != nil && (host.HostStatus == api.HOST_OFFLINE || !host.Enabled.Bool()) && diff --git a/pkg/util/logclient/consts.go b/pkg/util/logclient/consts.go index 1089ed9ff1..430ce888de 100644 --- a/pkg/util/logclient/consts.go +++ b/pkg/util/logclient/consts.go @@ -80,7 +80,7 @@ const ( ACT_DISK_CREATE_SNAPSHOT = "disk_create_snapshot" ACT_LB_ADD_BACKEND = "lb_add_backend" ACT_LB_REMOVE_BACKEND = "lb_remove_backend" - ACL_LB_SYNC_BACKEND_CONF = "同步后端服务器配置" + ACL_LB_SYNC_BACKEND_CONF = "lb_sync_backend_conf" ACT_LB_ADD_LISTENER_RULE = "lb_add_listener_rule" ACT_LB_REMOVE_LISTENER_RULE = "lb_remove_listener_rule" ACT_DELETE_BACKUP = "delete_backup" @@ -96,6 +96,8 @@ const ( ACT_VM_BLOCK_STREAM = "vm_block_stream" ACT_ATTACH_NETWORK = "attach_network" ACT_VM_CONVERT = "vm_convert" + ACT_FREEZE = "freeze" + ACT_UNFREEZE = "unfreeze" ACT_CACHED_IMAGE = "cached_image" diff --git a/pkg/util/logclient/consts_i18n.go b/pkg/util/logclient/consts_i18n.go index 5e0c9230df..9ad6fe3770 100644 --- a/pkg/util/logclient/consts_i18n.go +++ b/pkg/util/logclient/consts_i18n.go @@ -284,6 +284,7 @@ func init() { CN("移除后端服务器"), ) t.Set(ACL_LB_SYNC_BACKEND_CONF, i18n.NewTableEntry(). + EN("Lb Sycn Backend Conf"). CN("同步后端服务器配置"), ) t.Set(ACT_LB_ADD_LISTENER_RULE, i18n.NewTableEntry(). @@ -599,4 +600,13 @@ func init() { EN("Sync Record Sets"). CN("同步解析列表"), ) + + t.Set(ACT_FREEZE, i18n.NewTableEntry(). + EN("Freeze"). + CN("冻结资源"), + ) + t.Set(ACT_UNFREEZE, i18n.NewTableEntry(). + EN("Freeze"). + CN("解冻资源"), + ) }