From f2e0fcf4e9c23344354b68379cb4191f46181621 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Fri, 8 May 2020 23:15:16 +0800 Subject: [PATCH] fix: 1. vpc, cloudprovider should not violate the sharing limit of cloud account 2. sharing mode of host and attached local storage should be consistent. 3. cannot change owner of shared resource --- cmd/climc/shell/compute/cloudproviders.go | 9 +++++ cmd/climc/shell/compute/storages.go | 45 ++++++++++++++++++++++ cmd/climc/shell/compute/vpcs.go | 19 +++++++++ pkg/cloudcommon/db/domainresource.go | 4 ++ pkg/cloudcommon/db/virtualresource.go | 4 ++ pkg/compute/guestdrivers/managedvirtual.go | 2 +- pkg/compute/models/cloudproviders.go | 21 ++++++++++ pkg/compute/models/disks.go | 2 +- pkg/compute/models/hosts.go | 44 +++++++++------------ pkg/compute/models/networks.go | 2 +- pkg/compute/models/purge.go | 2 +- pkg/compute/models/storages.go | 15 ++++++++ pkg/compute/models/vpcs.go | 2 +- pkg/compute/models/wires.go | 2 +- 14 files changed, 142 insertions(+), 31 deletions(-) diff --git a/cmd/climc/shell/compute/cloudproviders.go b/cmd/climc/shell/compute/cloudproviders.go index 72413e1633..5c0e61ebd0 100644 --- a/cmd/climc/shell/compute/cloudproviders.go +++ b/cmd/climc/shell/compute/cloudproviders.go @@ -245,4 +245,13 @@ func init() { printObject(result) return nil }) + + R(&CloudproviderShowOptions{}, "cloud-provider-change-owner-candidate-domains", "Show candiate domains of a cloud provider changing owner", func(s *mcclient.ClientSession, args *CloudproviderShowOptions) error { + result, err := modules.Cloudproviders.GetSpecific(s, args.ID, "change-owner-candidate-domains", nil) + if err != nil { + return err + } + printObject(result) + return nil + }) } diff --git a/cmd/climc/shell/compute/storages.go b/cmd/climc/shell/compute/storages.go index 636f4a0769..203ad24b23 100644 --- a/cmd/climc/shell/compute/storages.go +++ b/cmd/climc/shell/compute/storages.go @@ -215,4 +215,49 @@ func init() { printObject(storage) return nil }) + + type StorageChangeOwnerOptions struct { + ID string `help:"ID or name of storage" json:"-"` + ProjectDomain string `json:"project_domain" help:"target domain"` + } + R(&StorageChangeOwnerOptions{}, "storage-change-owner", "Change owner domain of storage", func(s *mcclient.ClientSession, args *StorageChangeOwnerOptions) error { + if len(args.ProjectDomain) == 0 { + return fmt.Errorf("empty project_domain") + } + params := jsonutils.Marshal(args) + ret, err := modules.Storages.PerformAction(s, args.ID, "change-owner", params) + if err != nil { + return err + } + printObject(ret) + return nil + }) + + type StoragePublicOptions struct { + ID string `help:"ID or name of storage" json:"-"` + Scope string `help:"sharing scope" choices:"system|domain"` + SharedDomains []string `help:"share to domains"` + } + R(&StoragePublicOptions{}, "storage-public", "Make a storage public", func(s *mcclient.ClientSession, args *StoragePublicOptions) error { + params := jsonutils.Marshal(args) + result, err := modules.Storages.PerformAction(s, args.ID, "public", params) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type StoragePrivateOptions struct { + ID string `help:"ID or name of storage" json:"-"` + } + R(&StoragePrivateOptions{}, "storage-private", "Make a storage private", func(s *mcclient.ClientSession, args *StoragePrivateOptions) error { + params := jsonutils.Marshal(args) + result, err := modules.Storages.PerformAction(s, args.ID, "private", params) + if err != nil { + return err + } + printObject(result) + return nil + }) } diff --git a/cmd/climc/shell/compute/vpcs.go b/cmd/climc/shell/compute/vpcs.go index ec067eeee7..88ae862539 100644 --- a/cmd/climc/shell/compute/vpcs.go +++ b/cmd/climc/shell/compute/vpcs.go @@ -15,6 +15,8 @@ package compute import ( + "fmt" + "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/mcclient" @@ -218,4 +220,21 @@ func init() { printObject(result) return nil }) + + type VpcChangeOwnerOptions struct { + ID string `help:"ID or name of vpc" json:"-"` + ProjectDomain string `json:"project_domain" help:"target domain"` + } + R(&VpcChangeOwnerOptions{}, "vpc-change-owner", "Change owner domain of vpc", func(s *mcclient.ClientSession, args *VpcChangeOwnerOptions) error { + if len(args.ProjectDomain) == 0 { + return fmt.Errorf("empty project_domain") + } + params := jsonutils.Marshal(args) + ret, err := modules.Vpcs.PerformAction(s, args.ID, "change-owner", params) + if err != nil { + return err + } + printObject(ret) + return nil + }) } diff --git a/pkg/cloudcommon/db/domainresource.go b/pkg/cloudcommon/db/domainresource.go index 6ec13e48c0..8826c6a3ee 100644 --- a/pkg/cloudcommon/db/domainresource.go +++ b/pkg/cloudcommon/db/domainresource.go @@ -133,6 +133,10 @@ func (model *SDomainLevelResourceBase) AllowPerformChangeOwner(ctx context.Conte } func (model *SDomainLevelResourceBase) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) { + if model.GetIStandaloneModel().IsShared() { + return nil, errors.Wrap(httperrors.ErrForbidden, "cannot change owner of shared resource") + } + manager := model.GetModelManager() data := jsonutils.Marshal(input) diff --git a/pkg/cloudcommon/db/virtualresource.go b/pkg/cloudcommon/db/virtualresource.go index 2062d59dea..59373d1b78 100644 --- a/pkg/cloudcommon/db/virtualresource.go +++ b/pkg/cloudcommon/db/virtualresource.go @@ -258,6 +258,10 @@ func (model *SVirtualResourceBase) AllowPerformChangeOwner(ctx context.Context, } func (model *SVirtualResourceBase) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) { + if model.GetIStandaloneModel().IsShared() { + return nil, errors.Wrap(httperrors.ErrForbidden, "cannot change owner of shared resource") + } + manager := model.GetModelManager() data := jsonutils.Marshal(input) diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index e624c06897..975a65d9de 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -1054,7 +1054,7 @@ func (self *SManagedVirtualizedGuestDriver) chooseHostStorage( if len(storageIds) != 0 { return models.StorageManager.FetchStorageById(storageIds[0]) } - storages := host.GetAttachedStorages("") + storages := host.GetAttachedEnabledHostStorages(nil) for i := 0; i < len(storages); i += 1 { if storages[i].StorageType == backend { return &storages[i] diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 15a6abad6f..4bacfbe6c4 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -1524,3 +1524,24 @@ func (provider *SCloudprovider) IsSharable(reqUsrId mcclient.IIdentityProvider) } return false } + +func (provider *SCloudprovider) AllowGetDetailsChangeOwnerCandidateDomains(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return provider.DomainId == userCred.GetProjectDomainId() || db.IsAdminAllowGetSpec(userCred, provider, "change-owner-candidate-domains") +} + +func (provider *SCloudprovider) GetDetailsChangeOwnerCandidateDomains(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (apis.ChangeOwnerCandidateDomainsOutput, error) { + return db.IOwnerResourceBaseModelGetChangeOwnerCandidateDomains(provider) +} + +func (provider *SCloudprovider) GetChangeOwnerCandidateDomainIds() []string { + account := provider.GetCloudaccount() + if account.ShareMode == api.CLOUD_ACCOUNT_SHARE_MODE_ACCOUNT_DOMAIN { + return []string{account.DomainId} + } + // if account's public_scope=domain and share_mode=provider_domain, only allow to share to specific domains + if account.PublicScope == string(rbacutils.ScopeDomain) { + sharedDomains := account.GetSharedDomains() + return append(sharedDomains, account.DomainId) + } + return []string{} +} diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index 7ac896efa0..2099f0c9be 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -580,7 +580,7 @@ func (disk *SDisk) SetStorageByHost(hostId string, diskConfig *api.DiskConfig, s storage = host.GetLeastUsedStorage(backend) } else { // unlimited pulic cloud storages - storages := host.GetAttachedStorages("") + storages := host.GetAttachedEnabledHostStorages(nil) for _, s := range storages { if s.StorageType == backend { tmpS := s diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index fbd6a5ceed..e88f8b5229 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -888,11 +888,11 @@ func (self *SHost) GetFetchUrl(disableHttps bool) string { return fmt.Sprintf("%s://%s:%d", managerUrl.Scheme, strings.Split(managerUrl.Host, ":")[0], port+40000) } -func (self *SHost) GetAttachedStorages(storageType string) []SStorage { +func (self *SHost) GetAttachedEnabledHostStorages(storageType []string) []SStorage { return self._getAttachedStorages(tristate.False, tristate.True, storageType) } -func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled tristate.TriState, storageType string) []SStorage { +func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled tristate.TriState, storageType []string) []SStorage { storages := StorageManager.Query().SubQuery() hoststorages := HoststorageManager.Query().SubQuery() q := storages.Query() @@ -908,7 +908,7 @@ func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled t q = q.NotEquals("storage_type", api.STORAGE_BAREMETAL) } if len(storageType) > 0 { - q = q.Equals("storage_type", storageType) + q = q.In("storage_type", storageType) } q = q.Filter(sqlchemy.Equals(hoststorages.Field("host_id"), self.Id)) ret := make([]SStorage, 0) @@ -921,7 +921,7 @@ func (self *SHost) _getAttachedStorages(isBaremetal tristate.TriState, enabled t } func (self *SHost) SyncAttachedStorageStatus() { - storages := self.GetAttachedStorages("") + storages := self.GetAttachedEnabledHostStorages(nil) if storages != nil { for _, storage := range storages { storage.SyncStatusWithHosts() @@ -1212,26 +1212,15 @@ func (cap *SStorageCapacity) toCapacityInfo() api.SStorageCapacityInfo { func (self *SHost) GetAttachedLocalStorageCapacity() SStorageCapacity { ret := SStorageCapacity{} - storages := self.GetAttachedStorages("") + storages := self.GetAttachedEnabledHostStorages(api.HOST_STORAGE_LOCAL_TYPES) for _, s := range storages { - if !utils.IsInStringArray(s.StorageType, api.HOST_STORAGE_LOCAL_TYPES) { - continue - } ret.Add(s.getStorageCapacity()) } return ret } func (self *SHost) GetAttachedLocalStorages() []SStorage { - ret := make([]SStorage, 0) - storages := self.GetAttachedStorages("") - for _, s := range storages { - if !utils.IsInStringArray(s.StorageType, api.HOST_STORAGE_LOCAL_TYPES) { - continue - } - ret = append(ret, s) - } - return ret + return self.GetAttachedEnabledHostStorages(api.HOST_STORAGE_LOCAL_TYPES) } func _getLeastUsedStorage(storages []SStorage, backends []string) *SStorage { @@ -1267,7 +1256,7 @@ func getLeastUsedStorage(storages []SStorage, backend string) *SStorage { } func (self *SHost) GetLeastUsedStorage(backend string) *SStorage { - storages := self.GetAttachedStorages("") + storages := self.GetAttachedEnabledHostStorages(nil) if storages != nil { return getLeastUsedStorage(storages, backend) } @@ -2704,7 +2693,7 @@ func (self *SHost) Request(ctx context.Context, userCred mcclient.TokenCredentia } func (self *SHost) GetLocalStoragecache() *SStoragecache { - localStorages := self.GetAttachedStorages(api.STORAGE_LOCAL) + localStorages := self.GetAttachedLocalStorages() for i := 0; i < len(localStorages); i += 1 { sc := localStorages[i].GetStoragecache() if sc != nil { @@ -2715,7 +2704,7 @@ func (self *SHost) GetLocalStoragecache() *SStoragecache { } func (self *SHost) GetStoragecache() *SStoragecache { - localStorages := self.GetAttachedStorages("") + localStorages := self.GetAttachedEnabledHostStorages(nil) for i := 0; i < len(localStorages); i += 1 { sc := localStorages[i].GetStoragecache() if sc != nil { @@ -5075,15 +5064,20 @@ func (model *SHost) CustomizeCreate(ctx context.Context, userCred mcclient.Token } func (host *SHost) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) { - localStorages := host.GetAttachedLocalStorages() + ret, err := host.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input) + if err != nil { + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformChangeOwner") + } + + localStorages := host._getAttachedStorages(tristate.None, tristate.None, api.HOST_STORAGE_LOCAL_TYPES) for i := range localStorages { - _, err := localStorages[i].PerformChangeOwner(ctx, userCred, query, input) + _, err := localStorages[i].performChangeOwnerInternal(ctx, userCred, query, input) if err != nil { return nil, errors.Wrap(err, "local storage change owner") } } - return host.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input) + return ret, nil } func GetHostQuotaKeysFromCreateInput(input api.HostCreateInput) quotas.SDomainRegionalCloudResourceKeys { @@ -5125,7 +5119,7 @@ func (host *SHost) GetUsages() []db.IUsage { func (host *SHost) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicDomainInput) (jsonutils.JSONObject, error) { // perform public for all connected local storage - storages := host.GetAttachedLocalStorages() + storages := host._getAttachedStorages(tristate.None, tristate.None, api.HOST_STORAGE_LOCAL_TYPES) for i := range storages { _, err := storages[i].performPublicInternal(ctx, userCred, query, input) if err != nil { @@ -5137,7 +5131,7 @@ func (host *SHost) PerformPublic(ctx context.Context, userCred mcclient.TokenCre func (host *SHost) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) (jsonutils.JSONObject, error) { // perform private for all connected local storage - storages := host.GetAttachedLocalStorages() + storages := host._getAttachedStorages(tristate.None, tristate.None, api.HOST_STORAGE_LOCAL_TYPES) for i := range storages { _, err := storages[i].performPrivateInternal(ctx, userCred, query, input) if err != nil { diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 6bee7c59fc..929e65bac5 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -2567,7 +2567,7 @@ func (net *SNetwork) PerformStatus(ctx context.Context, userCred mcclient.TokenC func (net *SNetwork) GetChangeOwnerCandidateDomainIds() []string { candidates := [][]string{ - net.SSharableVirtualResourceBase.GetChangeOwnerCandidateDomainIds(), + db.ISharableChangeOwnerCandidateDomainIds(net), } wire := net.GetWire() if wire != nil { diff --git a/pkg/compute/models/purge.go b/pkg/compute/models/purge.go index faec8cc4e7..fef464fb36 100644 --- a/pkg/compute/models/purge.go +++ b/pkg/compute/models/purge.go @@ -94,7 +94,7 @@ func (host *SHost) purge(ctx context.Context, userCred mcclient.TokenCredential) } // clean all disks on locally attached storages - storages := host._getAttachedStorages(tristate.None, tristate.None, api.STORAGE_LOCAL) + storages := host._getAttachedStorages(tristate.None, tristate.None, api.HOST_STORAGE_LOCAL_TYPES) for i := range storages { err := storages[i].purgeDisks(ctx, userCred) if err != nil { diff --git a/pkg/compute/models/storages.go b/pkg/compute/models/storages.go index 2adc3ab142..5de41c906a 100644 --- a/pkg/compute/models/storages.go +++ b/pkg/compute/models/storages.go @@ -1458,6 +1458,21 @@ func (self *SStorage) StartDeleteRbdDisks(ctx context.Context, userCred mcclient return nil } +func (storage *SStorage) PerformChangeOwner(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) { + // not allow to perform public for locally connected storage + if storage.IsLocal() { + hosts := storage.GetAttachedHosts() + if len(hosts) > 0 { + return nil, errors.Wrap(httperrors.ErrForbidden, "not allow to change owner for local storage") + } + } + return storage.performChangeOwnerInternal(ctx, userCred, query, input) +} + +func (storage *SStorage) performChangeOwnerInternal(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeDomainOwnerInput) (jsonutils.JSONObject, error) { + return storage.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input) +} + func (storage *SStorage) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicDomainInput) (jsonutils.JSONObject, error) { // not allow to perform public for locally connected storage if storage.IsLocal() { diff --git a/pkg/compute/models/vpcs.go b/pkg/compute/models/vpcs.go index 00b7d40e7d..dc16828a6e 100644 --- a/pkg/compute/models/vpcs.go +++ b/pkg/compute/models/vpcs.go @@ -1093,7 +1093,7 @@ func (manager *SVpcManager) totalCount( func (vpc *SVpc) GetChangeOwnerCandidateDomainIds() []string { candidates := [][]string{ - vpc.SEnabledStatusInfrasResourceBase.GetChangeOwnerCandidateDomainIds(), + db.ISharableChangeOwnerCandidateDomainIds(vpc), } globalVpc, _ := vpc.GetGlobalVpc() if globalVpc != nil { diff --git a/pkg/compute/models/wires.go b/pkg/compute/models/wires.go index 6a30bc49cd..f006659b30 100644 --- a/pkg/compute/models/wires.go +++ b/pkg/compute/models/wires.go @@ -1012,7 +1012,7 @@ func (model *SWire) CustomizeCreate(ctx context.Context, userCred mcclient.Token func (wire *SWire) GetChangeOwnerCandidateDomainIds() []string { candidates := [][]string{ - wire.SInfrasResourceBase.GetChangeOwnerCandidateDomainIds(), + db.ISharableChangeOwnerCandidateDomainIds(wire), } vpc := wire.GetVpc() if vpc != nil {