diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index fa56800184..574c25e790 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -25,8 +25,6 @@ import ( "time" "unicode" - "gopkg.in/fatih/set.v0" - "yunion.io/x/cloudmux/pkg/cloudprovider" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -1465,313 +1463,6 @@ func (self *SGuest) StartDeleteGuestTask( return self.GetDriver().StartDeleteGuestTask(ctx, userCred, self, params, parentTaskId) } -// 绑定多个安全组 -func (self *SGuest) PerformAddSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestAddSecgroupInput) (jsonutils.JSONObject, error) { - if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { - return nil, httperrors.NewInputParameterError("Cannot add security groups in status %s", self.Status) - } - - maxCount := self.GetDriver().GetMaxSecurityGroupCount() - if maxCount == 0 { - return nil, httperrors.NewUnsupportOperationError("Cannot add security groups for hypervisor %s", self.Hypervisor) - } - - if len(input.SecgroupIds) == 0 { - return nil, httperrors.NewMissingParameterError("secgroup_ids") - } - - secgroups, err := self.GetSecgroups() - if err != nil { - return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetSecgroups")) - } - if len(secgroups)+len(input.SecgroupIds) > maxCount { - return nil, httperrors.NewUnsupportOperationError("guest %s band to up to %d security groups", self.Name, maxCount) - } - - secgroupIds := []string{} - for _, secgroup := range secgroups { - secgroupIds = append(secgroupIds, secgroup.Id) - } - - secgroupNames := []string{} - for _, secgroupId := range input.SecgroupIds { - secgrp, err := SecurityGroupManager.FetchByIdOrName(userCred, secgroupId) - if err != nil { - if errors.Cause(err) == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2("secgroup", secgroupId) - } - return nil, httperrors.NewGeneralError(errors.Wrapf(err, "SecurityGroupManager.FetchByIdOrName(%s)", secgroupId)) - } - - err = SecurityGroupManager.ValidateName(secgrp.GetName()) - if err != nil { - return nil, httperrors.NewInputParameterError("The secgroup name %s does not meet the requirements, please change the name", secgrp.GetName()) - } - - if utils.IsInStringArray(secgrp.GetId(), secgroupIds) { - return nil, httperrors.NewInputParameterError("security group %s has already been assigned to guest %s", secgrp.GetName(), self.Name) - } - secgroupIds = append(secgroupIds, secgrp.GetId()) - secgroupNames = append(secgroupNames, secgrp.GetName()) - } - - err = self.saveSecgroups(ctx, userCred, secgroupIds) - if err != nil { - return nil, httperrors.NewGeneralError(errors.Wrap(err, "saveSecgroups")) - } - - notes := map[string][]string{"secgroups": secgroupNames} - logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_ASSIGNSECGROUP, notes, userCred, true) - return nil, self.StartSyncTask(ctx, userCred, true, "") -} - -func (self *SGuest) saveDefaultSecgroupId(userCred mcclient.TokenCredential, secGrpId string, isAdmin bool) error { - if (!isAdmin && secGrpId != self.SecgrpId) || (isAdmin && secGrpId != self.AdminSecgrpId) { - diff, err := db.Update(self, func() error { - if isAdmin { - self.AdminSecgrpId = secGrpId - } else { - self.SecgrpId = secGrpId - } - return nil - }) - if err != nil { - return errors.Wrap(err, "db.Update") - } - db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred) - } - return nil -} - -func (self *SGuest) PerformRevokeSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestRevokeSecgroupInput) (jsonutils.JSONObject, error) { - if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { - return nil, httperrors.NewInputParameterError("Cannot revoke security groups in status %s", self.Status) - } - - if len(input.SecgroupIds) == 0 { - return nil, nil - } - - secgroups, err := self.GetSecgroups() - if err != nil { - return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetSecgroups")) - } - secgroupMaps := map[string]string{} - for _, secgroup := range secgroups { - secgroupMaps[secgroup.Id] = secgroup.Name - } - - secgroupNames := []string{} - for _, secgroupId := range input.SecgroupIds { - secgrp, err := SecurityGroupManager.FetchByIdOrName(userCred, secgroupId) - if err != nil { - if errors.Cause(err) == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2("secgroup", secgroupId) - } - return nil, httperrors.NewGeneralError(errors.Wrapf(err, "SecurityGroupManager.FetchByIdOrName(%s)", secgroupId)) - } - _, ok := secgroupMaps[secgrp.GetId()] - if !ok { - return nil, httperrors.NewInputParameterError("security group %s not assigned to guest %s", secgrp.GetName(), self.Name) - } - delete(secgroupMaps, secgrp.GetId()) - secgroupNames = append(secgroupNames, secgrp.GetName()) - } - - secgrpIds := []string{} - for secgroupId := range secgroupMaps { - secgrpIds = append(secgrpIds, secgroupId) - } - - err = self.saveSecgroups(ctx, userCred, secgrpIds) - if err != nil { - return nil, httperrors.NewGeneralError(errors.Wrap(err, "saveSecgroups")) - } - - notes := map[string][]string{"secgroups": secgroupNames} - logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_REVOKESECGROUP, notes, userCred, true) - return nil, self.StartSyncTask(ctx, userCred, true, "") -} - -func (self *SGuest) PerformRevokeAdminSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestRevokeSecgroupInput) (jsonutils.JSONObject, error) { - if !db.IsAdminAllowPerform(ctx, userCred, self, "revoke-admin-secgroup") { - return nil, httperrors.NewForbiddenError("not allow to revoke admin secgroup") - } - - if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { - return nil, httperrors.NewInputParameterError("Cannot assign security rules in status %s", self.Status) - } - - var notes string - adminSecgrpId := "" - if len(options.Options.DefaultAdminSecurityGroupId) > 0 { - adminSecgrp, _ := SecurityGroupManager.FetchSecgroupById(options.Options.DefaultAdminSecurityGroupId) - if adminSecgrp != nil { - adminSecgrpId = adminSecgrp.Id - notes = fmt.Sprintf("reset admin secgroup to %s(%s)", adminSecgrp.Name, adminSecgrp.Id) - } - } - if adminSecgrpId == "" { - notes = "clean admin secgroup" - } - - err := self.saveDefaultSecgroupId(userCred, adminSecgrpId, true) - if err != nil { - return nil, errors.Wrap(err, "saveDefaultSecgroupId") - } - - logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_REVOKESECGROUP, notes, userCred, true) - return nil, self.StartSyncTask(ctx, userCred, true, "") -} - -// +onecloud:swagger-gen-ignore -func (self *SGuest) PerformAssignSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestAssignSecgroupInput) (jsonutils.JSONObject, error) { - return self.performAssignSecgroup(ctx, userCred, query, input, false) -} - -func (self *SGuest) PerformAssignAdminSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestAssignSecgroupInput) (jsonutils.JSONObject, error) { - if !db.IsAdminAllowPerform(ctx, userCred, self, "assign-admin-secgroup") { - return nil, httperrors.NewForbiddenError("not allow to assign admin secgroup") - } - - return self.performAssignSecgroup(ctx, userCred, query, input, true) -} - -func (self *SGuest) performAssignSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestAssignSecgroupInput, isAdmin bool) (jsonutils.JSONObject, error) { - if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { - return nil, httperrors.NewInputParameterError("Cannot assign security rules in status %s", self.Status) - } - - if len(input.SecgroupId) == 0 { - return nil, httperrors.NewMissingParameterError("secgroup_id") - } - - secObj, err := validators.ValidateModel(userCred, SecurityGroupManager, &input.SecgroupId) - if err != nil { - return nil, err - } - - err = SecurityGroupManager.ValidateName(secObj.GetName()) - if err != nil { - return nil, httperrors.NewInputParameterError("The secgroup name %s does not meet the requirements, please change the name", secObj.GetName()) - } - - err = self.saveDefaultSecgroupId(userCred, input.SecgroupId, isAdmin) - if err != nil { - return nil, err - } - - notes := map[string]string{"name": secObj.GetName(), "id": secObj.GetId(), "is_admin": fmt.Sprintf("%v", isAdmin)} - logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_ASSIGNSECGROUP, notes, userCred, true) - return nil, self.StartSyncTask(ctx, userCred, true, "") -} - -// 全量覆盖安全组 -func (self *SGuest) PerformSetSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.GuestSetSecgroupInput) (jsonutils.JSONObject, error) { - if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { - return nil, httperrors.NewInputParameterError("Cannot set security rules in status %s", self.Status) - } - if len(input.SecgroupIds) == 0 { - return nil, httperrors.NewMissingParameterError("secgroup_ids") - } - - maxCount := self.GetDriver().GetMaxSecurityGroupCount() - if maxCount == 0 { - return nil, httperrors.NewUnsupportOperationError("Cannot set security group for this guest %s", self.Name) - } - - if len(input.SecgroupIds) > maxCount { - return nil, httperrors.NewUnsupportOperationError("guest %s band to up to %d security groups", self.Name, maxCount) - } - - secgroupIds := []string{} - secgroupNames := []string{} - for _, secgroupId := range input.SecgroupIds { - secgrp, err := SecurityGroupManager.FetchByIdOrName(userCred, secgroupId) - if err != nil { - if errors.Cause(err) == sql.ErrNoRows { - return nil, httperrors.NewResourceNotFoundError2("secgroup", secgroupId) - } - return nil, httperrors.NewGeneralError(errors.Wrapf(err, "FetchByIdOrName(%s)", secgroupId)) - } - - err = SecurityGroupManager.ValidateName(secgrp.GetName()) - if err != nil { - return nil, httperrors.NewInputParameterError("The secgroup name %s does not meet the requirements, please change the name", secgrp.GetName()) - } - - if !utils.IsInStringArray(secgrp.GetId(), secgroupIds) { - secgroupIds = append(secgroupIds, secgrp.GetId()) - secgroupNames = append(secgroupNames, secgrp.GetName()) - } - } - - err := self.saveSecgroups(ctx, userCred, secgroupIds) - if err != nil { - return nil, httperrors.NewGeneralError(errors.Wrapf(err, "saveSecgroups")) - } - - notes := map[string][]string{"secgroups": secgroupNames} - logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_SETSECGROUP, notes, userCred, true) - return nil, self.StartSyncTask(ctx, userCred, true, "") -} - -func (self *SGuest) GetGuestSecgroups() ([]SGuestsecgroup, error) { - gss := []SGuestsecgroup{} - q := GuestsecgroupManager.Query().Equals("guest_id", self.Id) - err := db.FetchModelObjects(GuestsecgroupManager, q, &gss) - if err != nil { - return nil, errors.Wrapf(err, "db.FetchModelObjects") - } - return gss, nil -} - -func (self *SGuest) saveSecgroups(ctx context.Context, userCred mcclient.TokenCredential, secgroupIds []string) error { - if len(secgroupIds) == 0 { - return self.RevokeAllSecgroups(ctx, userCred) - } - oldIds := set.New(set.ThreadSafe) - newIds := set.New(set.ThreadSafe) - gss, err := self.GetGuestSecgroups() - if err != nil { - return errors.Wrapf(err, "GetGuestSecgroups") - } - secgroupMaps := map[string]SGuestsecgroup{} - for i := range gss { - oldIds.Add(gss[i].SecgroupId) - secgroupMaps[gss[i].SecgroupId] = gss[i] - } - for i := 1; i < len(secgroupIds); i++ { - newIds.Add(secgroupIds[i]) - } - for _, removed := range set.Difference(oldIds, newIds).List() { - id := removed.(string) - gs, ok := secgroupMaps[id] - if ok { - err = gs.Delete(ctx, userCred) - if err != nil { - return errors.Wrapf(err, "Delete guest secgroup for guest %s secgroup %s", self.Name, id) - } - } - } - for _, added := range set.Difference(newIds, oldIds).List() { - id := added.(string) - err = self.newGuestSecgroup(ctx, id) - if err != nil { - return errors.Wrapf(err, "New guest secgroup for guest %s with secgroup %s", self.Name, id) - } - } - return self.saveDefaultSecgroupId(userCred, secgroupIds[0], false) -} - -func (self *SGuest) newGuestSecgroup(ctx context.Context, secgroupId string) error { - gs := &SGuestsecgroup{} - gs.SetModelManager(GuestsecgroupManager, gs) - gs.GuestId = self.Id - gs.SecgroupId = secgroupId - return GuestsecgroupManager.TableSpec().Insert(ctx, gs) -} - func (self *SGuest) PerformPurge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { err := self.ValidatePurgeCondition(ctx) if err != nil { @@ -2965,20 +2656,6 @@ func (self *SGuest) StartChangeConfigTask(ctx context.Context, userCred mcclient return nil } -func (self *SGuest) RevokeAllSecgroups(ctx context.Context, userCred mcclient.TokenCredential) error { - gss, err := self.GetGuestSecgroups() - if err != nil { - return errors.Wrapf(err, "GetGuestSecgroups") - } - for i := range gss { - err = gss[i].Delete(ctx, userCred) - if err != nil { - return errors.Wrap(err, "Delete") - } - } - return self.saveDefaultSecgroupId(userCred, options.Options.DefaultSecurityGroupId, false) -} - func (self *SGuest) DoPendingDelete(ctx context.Context, userCred mcclient.TokenCredential) { eip, _ := self.GetEipOrPublicIp() if eip != nil { diff --git a/pkg/compute/models/guest_secgroups.go b/pkg/compute/models/guest_secgroups.go new file mode 100644 index 0000000000..978b3ea8cf --- /dev/null +++ b/pkg/compute/models/guest_secgroups.go @@ -0,0 +1,434 @@ +// 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" + + "gopkg.in/fatih/set.v0" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "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/validators" + "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/logclient" +) + +// 绑定多个安全组 +func (self *SGuest) PerformAddSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestAddSecgroupInput, +) (jsonutils.JSONObject, error) { + if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { + return nil, httperrors.NewInputParameterError("Cannot add security groups in status %s", self.Status) + } + + maxCount := self.GetDriver().GetMaxSecurityGroupCount() + if maxCount == 0 { + return nil, httperrors.NewUnsupportOperationError("Cannot add security groups for hypervisor %s", self.Hypervisor) + } + + if len(input.SecgroupIds) == 0 { + return nil, httperrors.NewMissingParameterError("secgroup_ids") + } + + secgroups, err := self.GetSecgroups() + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetSecgroups")) + } + if len(secgroups)+len(input.SecgroupIds) > maxCount { + return nil, httperrors.NewUnsupportOperationError("guest %s band to up to %d security groups", self.Name, maxCount) + } + + secgroupIds := []string{} + for _, secgroup := range secgroups { + secgroupIds = append(secgroupIds, secgroup.Id) + } + + reg, err := self.getRegion() + if err != nil { + return nil, errors.Wrap(err, "getRegion") + } + supportPeerSecgrp := reg.GetDriver().IsSupportPeerSecgroup() + + secgroupNames := []string{} + for _, secgroupId := range input.SecgroupIds { + secgrp, err := SecurityGroupManager.FetchByIdOrName(userCred, secgroupId) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2("secgroup", secgroupId) + } + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "SecurityGroupManager.FetchByIdOrName(%s)", secgroupId)) + } + + err = SecurityGroupManager.ValidateName(secgrp.GetName()) + if err != nil { + return nil, httperrors.NewInputParameterError("The secgroup name %s does not meet the requirements, please change the name", secgrp.GetName()) + } + + if utils.IsInStringArray(secgrp.GetId(), secgroupIds) { + return nil, httperrors.NewInputParameterError("security group %s has already been assigned to guest %s", secgrp.GetName(), self.Name) + } + + hasPeerSecgrp, err := secgrp.(*SSecurityGroup).HasPeerSecgroup() + if err != nil { + return nil, errors.Wrap(err, "secgrp.HasPeerSecgroup") + } + if hasPeerSecgrp && !supportPeerSecgrp { + return nil, errors.Wrap(httperrors.ErrNotSupported, "guest not support peer security group") + } + + secgroupIds = append(secgroupIds, secgrp.GetId()) + secgroupNames = append(secgroupNames, secgrp.GetName()) + } + + err = self.saveSecgroups(ctx, userCred, secgroupIds) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "saveSecgroups")) + } + + notes := map[string][]string{"secgroups": secgroupNames} + logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_ASSIGNSECGROUP, notes, userCred, true) + return nil, self.StartSyncTask(ctx, userCred, true, "") +} + +func (self *SGuest) saveDefaultSecgroupId(userCred mcclient.TokenCredential, secGrpId string, isAdmin bool) error { + if (!isAdmin && secGrpId != self.SecgrpId) || (isAdmin && secGrpId != self.AdminSecgrpId) { + diff, err := db.Update(self, func() error { + if isAdmin { + self.AdminSecgrpId = secGrpId + } else { + self.SecgrpId = secGrpId + } + return nil + }) + if err != nil { + return errors.Wrap(err, "db.Update") + } + db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred) + } + return nil +} + +func (self *SGuest) PerformRevokeSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestRevokeSecgroupInput, +) (jsonutils.JSONObject, error) { + if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { + return nil, httperrors.NewInputParameterError("Cannot revoke security groups in status %s", self.Status) + } + + if len(input.SecgroupIds) == 0 { + return nil, nil + } + + secgroups, err := self.GetSecgroups() + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetSecgroups")) + } + secgroupMaps := map[string]string{} + for _, secgroup := range secgroups { + secgroupMaps[secgroup.Id] = secgroup.Name + } + + secgroupNames := []string{} + for _, secgroupId := range input.SecgroupIds { + secgrp, err := SecurityGroupManager.FetchByIdOrName(userCred, secgroupId) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2("secgroup", secgroupId) + } + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "SecurityGroupManager.FetchByIdOrName(%s)", secgroupId)) + } + _, ok := secgroupMaps[secgrp.GetId()] + if !ok { + return nil, httperrors.NewInputParameterError("security group %s not assigned to guest %s", secgrp.GetName(), self.Name) + } + delete(secgroupMaps, secgrp.GetId()) + secgroupNames = append(secgroupNames, secgrp.GetName()) + } + + secgrpIds := []string{} + for secgroupId := range secgroupMaps { + secgrpIds = append(secgrpIds, secgroupId) + } + + err = self.saveSecgroups(ctx, userCred, secgrpIds) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "saveSecgroups")) + } + + notes := map[string][]string{"secgroups": secgroupNames} + logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_REVOKESECGROUP, notes, userCred, true) + return nil, self.StartSyncTask(ctx, userCred, true, "") +} + +func (self *SGuest) PerformRevokeAdminSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestRevokeSecgroupInput, +) (jsonutils.JSONObject, error) { + if !db.IsAdminAllowPerform(ctx, userCred, self, "revoke-admin-secgroup") { + return nil, httperrors.NewForbiddenError("not allow to revoke admin secgroup") + } + + if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { + return nil, httperrors.NewInputParameterError("Cannot assign security rules in status %s", self.Status) + } + + var notes string + adminSecgrpId := "" + if len(options.Options.DefaultAdminSecurityGroupId) > 0 { + adminSecgrp, _ := SecurityGroupManager.FetchSecgroupById(options.Options.DefaultAdminSecurityGroupId) + if adminSecgrp != nil { + adminSecgrpId = adminSecgrp.Id + notes = fmt.Sprintf("reset admin secgroup to %s(%s)", adminSecgrp.Name, adminSecgrp.Id) + } + } + if adminSecgrpId == "" { + notes = "clean admin secgroup" + } + + err := self.saveDefaultSecgroupId(userCred, adminSecgrpId, true) + if err != nil { + return nil, errors.Wrap(err, "saveDefaultSecgroupId") + } + + logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_REVOKESECGROUP, notes, userCred, true) + return nil, self.StartSyncTask(ctx, userCred, true, "") +} + +// +onecloud:swagger-gen-ignore +func (self *SGuest) PerformAssignSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestAssignSecgroupInput, +) (jsonutils.JSONObject, error) { + return self.performAssignSecgroup(ctx, userCred, query, input, false) +} + +func (self *SGuest) PerformAssignAdminSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestAssignSecgroupInput, +) (jsonutils.JSONObject, error) { + if !db.IsAdminAllowPerform(ctx, userCred, self, "assign-admin-secgroup") { + return nil, httperrors.NewForbiddenError("not allow to assign admin secgroup") + } + + return self.performAssignSecgroup(ctx, userCred, query, input, true) +} + +func (self *SGuest) performAssignSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestAssignSecgroupInput, + isAdmin bool, +) (jsonutils.JSONObject, error) { + if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { + return nil, httperrors.NewInputParameterError("Cannot assign security rules in status %s", self.Status) + } + + if len(input.SecgroupId) == 0 { + return nil, httperrors.NewMissingParameterError("secgroup_id") + } + + secObj, err := validators.ValidateModel(userCred, SecurityGroupManager, &input.SecgroupId) + if err != nil { + return nil, err + } + + err = SecurityGroupManager.ValidateName(secObj.GetName()) + if err != nil { + return nil, httperrors.NewInputParameterError("The secgroup name %s does not meet the requirements, please change the name", secObj.GetName()) + } + + reg, err := self.getRegion() + if err != nil { + return nil, errors.Wrap(err, "getRegion") + } + supportPeerSecgrp := reg.GetDriver().IsSupportPeerSecgroup() + hasPeerSecgrp, err := secObj.(*SSecurityGroup).HasPeerSecgroup() + if err != nil { + return nil, errors.Wrap(err, "secgrp.HasPeerSecgroup") + } + if hasPeerSecgrp && !supportPeerSecgrp { + return nil, errors.Wrap(httperrors.ErrNotSupported, "guest not support peer security group") + } + + err = self.saveDefaultSecgroupId(userCred, input.SecgroupId, isAdmin) + if err != nil { + return nil, err + } + + notes := map[string]string{"name": secObj.GetName(), "id": secObj.GetId(), "is_admin": fmt.Sprintf("%v", isAdmin)} + logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_ASSIGNSECGROUP, notes, userCred, true) + return nil, self.StartSyncTask(ctx, userCred, true, "") +} + +// 全量覆盖安全组 +func (self *SGuest) PerformSetSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestSetSecgroupInput, +) (jsonutils.JSONObject, error) { + if !utils.IsInStringArray(self.Status, []string{api.VM_READY, api.VM_RUNNING, api.VM_SUSPEND}) { + return nil, httperrors.NewInputParameterError("Cannot set security rules in status %s", self.Status) + } + if len(input.SecgroupIds) == 0 { + return nil, httperrors.NewMissingParameterError("secgroup_ids") + } + + maxCount := self.GetDriver().GetMaxSecurityGroupCount() + if maxCount == 0 { + return nil, httperrors.NewUnsupportOperationError("Cannot set security group for this guest %s", self.Name) + } + + if len(input.SecgroupIds) > maxCount { + return nil, httperrors.NewUnsupportOperationError("guest %s band to up to %d security groups", self.Name, maxCount) + } + + reg, err := self.getRegion() + if err != nil { + return nil, errors.Wrap(err, "getRegion") + } + supportPeerSecgrp := reg.GetDriver().IsSupportPeerSecgroup() + + secgroupIds := []string{} + secgroupNames := []string{} + for _, secgroupId := range input.SecgroupIds { + secgrp, err := SecurityGroupManager.FetchByIdOrName(userCred, secgroupId) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError2("secgroup", secgroupId) + } + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "FetchByIdOrName(%s)", secgroupId)) + } + + err = SecurityGroupManager.ValidateName(secgrp.GetName()) + if err != nil { + return nil, httperrors.NewInputParameterError("The secgroup name %s does not meet the requirements, please change the name", secgrp.GetName()) + } + + if !utils.IsInStringArray(secgrp.GetId(), secgroupIds) { + hasPeerSecgrp, err := secgrp.(*SSecurityGroup).HasPeerSecgroup() + if err != nil { + return nil, errors.Wrap(err, "secgrp.HasPeerSecgroup") + } + if hasPeerSecgrp && !supportPeerSecgrp { + return nil, errors.Wrap(httperrors.ErrNotSupported, "guest not support peer security group") + } + + secgroupIds = append(secgroupIds, secgrp.GetId()) + secgroupNames = append(secgroupNames, secgrp.GetName()) + } + } + + err = self.saveSecgroups(ctx, userCred, secgroupIds) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "saveSecgroups")) + } + + notes := map[string][]string{"secgroups": secgroupNames} + logclient.AddActionLogWithContext(ctx, self, logclient.ACT_VM_SETSECGROUP, notes, userCred, true) + return nil, self.StartSyncTask(ctx, userCred, true, "") +} + +func (self *SGuest) GetGuestSecgroups() ([]SGuestsecgroup, error) { + gss := []SGuestsecgroup{} + q := GuestsecgroupManager.Query().Equals("guest_id", self.Id) + err := db.FetchModelObjects(GuestsecgroupManager, q, &gss) + if err != nil { + return nil, errors.Wrapf(err, "db.FetchModelObjects") + } + return gss, nil +} + +func (self *SGuest) saveSecgroups(ctx context.Context, userCred mcclient.TokenCredential, secgroupIds []string) error { + if len(secgroupIds) == 0 { + return self.RevokeAllSecgroups(ctx, userCred) + } + oldIds := set.New(set.ThreadSafe) + newIds := set.New(set.ThreadSafe) + gss, err := self.GetGuestSecgroups() + if err != nil { + return errors.Wrapf(err, "GetGuestSecgroups") + } + secgroupMaps := map[string]SGuestsecgroup{} + for i := range gss { + oldIds.Add(gss[i].SecgroupId) + secgroupMaps[gss[i].SecgroupId] = gss[i] + } + for i := 1; i < len(secgroupIds); i++ { + newIds.Add(secgroupIds[i]) + } + for _, removed := range set.Difference(oldIds, newIds).List() { + id := removed.(string) + gs, ok := secgroupMaps[id] + if ok { + err = gs.Delete(ctx, userCred) + if err != nil { + return errors.Wrapf(err, "Delete guest secgroup for guest %s secgroup %s", self.Name, id) + } + } + } + for _, added := range set.Difference(newIds, oldIds).List() { + id := added.(string) + err = self.newGuestSecgroup(ctx, id) + if err != nil { + return errors.Wrapf(err, "New guest secgroup for guest %s with secgroup %s", self.Name, id) + } + } + return self.saveDefaultSecgroupId(userCred, secgroupIds[0], false) +} + +func (self *SGuest) newGuestSecgroup(ctx context.Context, secgroupId string) error { + gs := &SGuestsecgroup{} + gs.SetModelManager(GuestsecgroupManager, gs) + gs.GuestId = self.Id + gs.SecgroupId = secgroupId + return GuestsecgroupManager.TableSpec().Insert(ctx, gs) +} + +func (self *SGuest) RevokeAllSecgroups(ctx context.Context, userCred mcclient.TokenCredential) error { + gss, err := self.GetGuestSecgroups() + if err != nil { + return errors.Wrapf(err, "GetGuestSecgroups") + } + for i := range gss { + err = gss[i].Delete(ctx, userCred) + if err != nil { + return errors.Wrap(err, "Delete") + } + } + return self.saveDefaultSecgroupId(userCred, options.Options.DefaultSecurityGroupId, false) +} diff --git a/pkg/compute/models/secgroupcache.go b/pkg/compute/models/secgroupcache.go index 35efae14c8..54b142cb6e 100644 --- a/pkg/compute/models/secgroupcache.go +++ b/pkg/compute/models/secgroupcache.go @@ -183,6 +183,11 @@ func (self *SSecurityGroupCache) GetIRegion(ctx context.Context) (cloudprovider. return provider.GetIRegionById(region.ExternalId) } +func (sgc *SSecurityGroupCache) IsSupportPeerSecgroup() bool { + driver := GetRegionDriver(sgc.GetProviderName()) + return driver.IsSupportPeerSecgroup() +} + func (manager *SSecurityGroupCacheManager) FilterByOwner(q *sqlchemy.SQuery, userCred mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery { if userCred != nil { sq := SecurityGroupManager.Query("id") @@ -770,7 +775,7 @@ func (self *SSecurityGroupCache) CreateISecurityGroup(ctx context.Context) (clou return iSecgroup, nil } -func (self *SSecurityGroupCache) GetSecuritRuleSet(ctx context.Context) (cloudprovider.SecurityRuleSet, []SSecurityGroupCache, error) { +func (self *SSecurityGroupCache) getSecurityRuleSet(ctx context.Context) (cloudprovider.SecurityRuleSet, []SSecurityGroupCache, error) { secgroup, err := self.GetSecgroup() if err != nil { return nil, nil, errors.Wrapf(err, "GetSecgroup") @@ -807,7 +812,7 @@ func (self *SSecurityGroupCache) convertRules(ctx context.Context, rules []SSecu if err != nil { return nil, nil, errors.Wrapf(err, "toRule") } - peerId := "" + peerExtId := "" if len(rules[i].PeerSecgroupId) > 0 { _peerSecgroup, err := SecurityGroupManager.FetchById(rules[i].PeerSecgroupId) if err != nil { @@ -821,13 +826,13 @@ func (self *SSecurityGroupCache) convertRules(ctx context.Context, rules []SSecu for _, cache := range peerCaches { if cache.ManagerId == self.ManagerId && cache.VpcId == self.VpcId && len(cache.ExternalId) > 0 && (!driver.IsPeerSecgroupWithSameProject() || cache.ExternalProjectId == self.ExternalProjectId) { - peerId = cache.ExternalId + peerExtId = cache.ExternalId break } } - if len(peerId) == 0 { - cache, err := SecurityGroupCacheManager.newCache(context.TODO(), peerSecgroup.Id, peerSecgroup.Name, self.VpcId, self.CloudregionId, self.ManagerId, self.ExternalProjectId) + if len(peerExtId) == 0 { + cache, err := SecurityGroupCacheManager.newCache(ctx, peerSecgroup.Id, peerSecgroup.Name, self.VpcId, self.CloudregionId, self.ManagerId, self.ExternalProjectId) if err != nil { return nil, nil, errors.Wrapf(err, "SecurityGroupCacheManager.newCache") } @@ -835,11 +840,15 @@ func (self *SSecurityGroupCache) convertRules(ctx context.Context, rules []SSecu if err != nil { return nil, nil, errors.Wrapf(err, "cache.CreateISecurityGroup") } - peerId = iSecgroup.GetGlobalId() + peerExtId = iSecgroup.GetGlobalId() caches = append(caches, *cache) } } - ruleSet = append(ruleSet, cloudprovider.SecurityRule{SecurityRule: *rule, ExternalId: rules[i].Id, PeerSecgroupId: peerId}) + ruleSet = append(ruleSet, cloudprovider.SecurityRule{ + SecurityRule: *rule, + ExternalId: rules[i].Id, + PeerSecgroupId: peerExtId, + }) } return ruleSet, caches, nil } @@ -862,9 +871,9 @@ func (self *SSecurityGroupCache) SyncRules(ctx context.Context, skipSyncRule boo return errors.Wrapf(err, "iSecgroup.GetRules") } - localRules, caches, err := self.GetSecuritRuleSet(ctx) + localRules, caches, err := self.getSecurityRuleSet(ctx) if err != nil { - return errors.Wrapf(err, "GetSecuritRuleSet") + return errors.Wrapf(err, "getSecurityRuleSet") } src := cloudprovider.NewSecRuleInfo(GetRegionDriver(api.CLOUD_PROVIDER_ONECLOUD)) diff --git a/pkg/compute/models/secgrouprules.go b/pkg/compute/models/secgrouprules.go index c0eb9fad23..6b3e34802a 100644 --- a/pkg/compute/models/secgrouprules.go +++ b/pkg/compute/models/secgrouprules.go @@ -335,6 +335,17 @@ func (self *SSecurityGroupRule) ValidateUpdateData(ctx context.Context, userCred if input.PeerSecgroupId == self.Id { return input, httperrors.NewInputParameterError("peer_secgroup_id can not point to secgroup self") } + // verify whether cache support peer secgroup + sg := self.GetSecGroup() + caches, err := sg.GetSecurityGroupCaches() + if err != nil { + return input, errors.Wrap(err, "sg.GetSecurityGroupCaches") + } + for _, c := range caches { + if !c.IsSupportPeerSecgroup() { + return input, errors.Wrapf(httperrors.ErrConflict, "the security group has been assigned to a provider not support peer security group") + } + } } err := input.Check() diff --git a/pkg/compute/models/secgroups.go b/pkg/compute/models/secgroups.go index 26323074db..b83e345c2b 100644 --- a/pkg/compute/models/secgroups.go +++ b/pkg/compute/models/secgroups.go @@ -690,11 +690,15 @@ func (self *SSecurityGroup) GetSecuritRuleSet() (cloudprovider.SecurityRuleSet, if err != nil { return nil, errors.Wrapf(err, "toRule") } - ruleSet = append(ruleSet, cloudprovider.SecurityRule{SecurityRule: *rule, ExternalId: rules[i].Id}) + ruleSet = append(ruleSet, cloudprovider.SecurityRule{ + SecurityRule: *rule, + ExternalId: rules[i].Id, + }) } return ruleSet, nil } +/* func (self *SSecurityGroup) GetSecRules() ([]secrules.SecurityRule, error) { rules := make([]secrules.SecurityRule, 0) _rules, err := self.getSecurityRules() @@ -711,6 +715,7 @@ func (self *SSecurityGroup) GetSecRules() ([]secrules.SecurityRule, error) { } return rules, nil } +*/ func (self *SSecurityGroup) getSecurityRuleString() (string, error) { secgrouprules, err := self.getSecurityRules() @@ -777,9 +782,30 @@ func (self *SSecurityGroup) PerformCacheSecgroup(ctx context.Context, userCred m return nil, httperrors.NewInputParameterError("Not support cache classic security group") } + hasPeerSg, err := self.HasPeerSecgroup() + if err != nil { + return nil, errors.Wrap(err, "HasPeerSecgroup") + } + if hasPeerSg && !region.GetDriver().IsSupportPeerSecgroup() { + return nil, httperrors.NewConflictError("target region not support peer secgroup") + } + return nil, self.StartSecurityGroupCacheTask(ctx, userCred, vpc.Id, classic, "") } +func (sg *SSecurityGroup) HasPeerSecgroup() (bool, error) { + rules, err := sg.getSecurityRules() + if err != nil { + return false, errors.Wrap(err, "GetSecRules") + } + for _, r := range rules { + if len(r.PeerSecgroupId) > 0 { + return true, nil + } + } + return false, nil +} + func (self *SSecurityGroup) StartSecurityGroupCacheTask(ctx context.Context, userCred mcclient.TokenCredential, vpcId string, classic bool, parentTaskId string) error { params := jsonutils.NewDict() params.Add(jsonutils.NewString(vpcId), "vpc_id") @@ -953,16 +979,18 @@ func (self *SSecurityGroup) PerformMerge(ctx context.Context, userCred mcclient. } func (self *SSecurityGroup) GetAllowList() (secrules.SecurityRuleSet, secrules.SecurityRuleSet, error) { - in, out := secrules.SecurityRuleSet{*secrules.MustParseSecurityRule("in:deny any")}, secrules.SecurityRuleSet{*secrules.MustParseSecurityRule("out:allow any")} - rules, err := self.GetSecRules() + in := secrules.SecurityRuleSet{*secrules.MustParseSecurityRule("in:deny any")} + out := secrules.SecurityRuleSet{*secrules.MustParseSecurityRule("out:allow any")} + rules, err := self.getSecurityRules() if err != nil { return in, out, errors.Wrapf(err, "GetSecRules") } for i := range rules { + r, _ := rules[i].toRule() if rules[i].Direction == secrules.DIR_IN { - in = append(in, rules[i]) + in = append(in, *r) } else { - in = append(in, rules[i]) + out = append(out, *r) } } return in.AllowList(), out.AllowList(), nil