fix: secgroup peer secgroup constraint (#15111)

Co-authored-by: QIU Jian <qiujian@yunionyun.com>
This commit is contained in:
Jian Qiu
2022-11-07 02:15:43 +08:00
committed by GitHub
co-authored by QIU Jian
parent ec53faee26
commit fea06ca2d2
5 changed files with 496 additions and 337 deletions
-323
View File
@@ -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 {
+434
View File
@@ -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)
}
+18 -9
View File
@@ -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))
+11
View File
@@ -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()
+33 -5
View File
@@ -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