From 73f71776debf5ca70ea86def786ef8814af48399 Mon Sep 17 00:00:00 2001 From: wanyaoqi <18528551+wanyaoqi@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:31:44 +0800 Subject: [PATCH] feat(region,host,vpcagent): allow guest network dedicate secgroups (#24261) --- cmd/climc/shell/compute/secgroups.go | 3 + cmd/climc/shell/compute/servers.go | 3 + pkg/apis/compute/api.go | 3 + pkg/apis/compute/guestnetwork.go | 19 + pkg/apis/compute/guests.go | 36 +- pkg/apis/compute/secgroup.go | 44 +- pkg/cloudcommon/cmdline/parser.go | 2 + pkg/compute/models/guest_actions.go | 13 + pkg/compute/models/guest_queries.go | 83 +++ pkg/compute/models/guest_secgroups.go | 14 + pkg/compute/models/guestnetworks.go | 15 + pkg/compute/models/guestnetworksecgroups.go | 644 ++++++++++++++++++ pkg/compute/models/guestresource.go | 4 +- pkg/compute/models/guests.go | 63 +- pkg/compute/models/secgroupresource.go | 1 + pkg/compute/models/secgroups.go | 21 + pkg/compute/service/handlers.go | 1 + pkg/compute/tasks/guest/guest_delete_task.go | 1 + pkg/hostman/guestman/desc/desc.go | 8 +- .../compute/mod_servernetworksecgroups.go | 33 + pkg/mcclient/options/compute/secgroup.go | 13 + pkg/mcclient/options/compute/servers.go | 14 + pkg/vpcagent/models/models.go | 19 + pkg/vpcagent/models/modelset.go | 58 +- pkg/vpcagent/models/modelsets.go | 120 ++-- pkg/vpcagent/models/secgrouprules_sort.go | 31 + pkg/vpcagent/ovn/keeper.go | 2 +- 27 files changed, 1191 insertions(+), 77 deletions(-) create mode 100644 pkg/compute/models/guestnetworksecgroups.go create mode 100644 pkg/mcclient/modules/compute/mod_servernetworksecgroups.go diff --git a/cmd/climc/shell/compute/secgroups.go b/cmd/climc/shell/compute/secgroups.go index 1f8d85012b..3f6135604a 100644 --- a/cmd/climc/shell/compute/secgroups.go +++ b/cmd/climc/shell/compute/secgroups.go @@ -36,4 +36,7 @@ func init() { cmd.Perform("change-owner", &options.SecgroupChangeOwnerOptions{}) cmd.Perform("import-rules", &options.SecgroupImportRulesOptions{}) cmd.PerformClass("clean", &options.SecgroupCleanOptions{}) + + scmd := shell.NewResourceCmd(&modules.Servernetworksecgroups) + scmd.List(&options.ServerNetworkSecgroupListOptions{}) } diff --git a/cmd/climc/shell/compute/servers.go b/cmd/climc/shell/compute/servers.go index 5bd87c6387..b0704c5a4f 100644 --- a/cmd/climc/shell/compute/servers.go +++ b/cmd/climc/shell/compute/servers.go @@ -149,6 +149,9 @@ func init() { cmd.Get("numa-info", new(options.ServerIdOptions)) cmd.BatchPerform("screen-dump", new(options.ServerIdsOptions)) cmd.Perform("set-network-num-queues", new(options.ServerSetNetworkNumQueues)) + cmd.Perform("set-network-secgroup", new(options.ServerNetworkSecGroupsOptions)) + cmd.Perform("add-network-secgroup", new(options.ServerNetworkSecGroupsOptions)) + cmd.Perform("revoke-network-secgroup", new(options.ServerNetworkSecGroupsOptions)) cmd.GetProperty(&options.ServerStatusStatisticsOptions{}) cmd.GetProperty(&options.ServerProjectStatisticsOptions{}) diff --git a/pkg/apis/compute/api.go b/pkg/apis/compute/api.go index 52d8721458..8d3f72c18e 100644 --- a/pkg/apis/compute/api.go +++ b/pkg/apis/compute/api.go @@ -130,6 +130,9 @@ type NetworkConfig struct { Domain string `json:"domain_id"` Ifname string `json:"ifname"` Schedtags []*SchedtagConfig `json:"schedtags"` + + // network secgroups + Secgroups []string `json:"secgroups"` } type AttachNetworkInput struct { diff --git a/pkg/apis/compute/guestnetwork.go b/pkg/apis/compute/guestnetwork.go index 4ad1e3dc45..a2e053eb1f 100644 --- a/pkg/apis/compute/guestnetwork.go +++ b/pkg/apis/compute/guestnetwork.go @@ -20,6 +20,8 @@ import ( "yunion.io/x/cloudmux/pkg/apis/compute" "yunion.io/x/jsonutils" "yunion.io/x/pkg/gotypes" + + "yunion.io/x/onecloud/pkg/apis" ) type GuestnetworkDetails struct { @@ -82,6 +84,12 @@ type GuestnetworkShortDesc struct { Index int `json:"index"` } +type GuestnetworkSecgroupShortDesc struct { + NetworkIndex int `json:"network_index"` + Secgroups []apis.StandaloneShortDesc `json:"secgroups"` + Mac string `json:"mac"` +} + type GuestnetworkListInput struct { GuestJointsListInput @@ -148,6 +156,9 @@ type GuestnetworkBaseDesc struct { WireId string `json:"wire_id"` Interface string `json:"interface"` + Secgroups []*SecgroupJsonDesc `json:"secgroups"` + SecurityRules string `json:"security_rules"` + Vpc struct { Id string `json:"id"` Provider string `json:"provider"` @@ -183,6 +194,14 @@ type GuestnetworkJsonDesc struct { LinkUp bool `json:"link_up"` } +type GuestnetworkSecgroupDesc struct { + Secgroups []*SecgroupJsonDesc `json:"secgroups"` + SecurityRules string `json:"security_rules"` + + Index int `json:"index"` + Mac string `json:"mac"` +} + type SNicTrafficRecord struct { RxTraffic int64 TxTraffic int64 diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index b4701cdda1..3752f92025 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -256,6 +256,9 @@ type ServerDetails struct { // 关联主安全组 Secgroup string `json:"secgroup"` + // 网卡级别安全组 + NetworkSecgroups []GuestnetworkSecgroupShortDesc `json:"network_secgroups"` + // 浮动IP Eip string `json:"eip"` // 浮动IP类型 @@ -571,12 +574,32 @@ type GuestSetSecgroupInput struct { SecgroupIds []string `json:"secgroup_ids"` } +type GuestSetNetworkSecgroupInput struct { + // 安全组Id列表 + // 实例必须处于运行,休眠或者关机状态 + SecgroupIds []string `json:"secgroup_ids"` + + // 虚机网卡 index 或者 mac 地址 + NetworkIndex *int `json:"network_index"` + MacAddr string `json:"mac_addr"` +} + type GuestRevokeSecgroupInput struct { // 安全组Id列表 // 实例必须处于运行,休眠或者关机状态 SecgroupIds []string `json:"secgroup_ids"` } +type GuestRevokeNetworkSecgroupInput struct { + // 安全组Id列表 + // 实例必须处于运行,休眠或者关机状态 + SecgroupIds []string `json:"secgroup_ids"` + + // 虚机网卡 index 或者 mac 地址 + NetworkIndex *int `json:"network_index"` + MacAddr string `json:"mac_addr"` +} + type GuestAssignSecgroupInput struct { // 安全组Id // 实例必须处于运行,休眠或者关机状态 @@ -606,6 +629,12 @@ type GuestAddSecgroupInput struct { SecgroupIds []string `json:"secgroup_ids"` } +type GuestNetworkAddSecgroupInput struct { + SecgroupIds []string `json:"secgroup_ids"` + + NetworkIndex *int `json:"network_index"` +} + type ServerRemoteUpdateInput struct { // 是否覆盖替换所有标签 ReplaceTags *bool `json:"replace_tags" help:"replace all remote tags"` @@ -941,9 +970,10 @@ type GuestJsonDesc struct { NetworkRoles []string `json:"network_roles"` - Secgroups []*SecgroupJsonDesc `json:"secgroups"` - SecurityRules string `json:"security_rules"` - AdminSecurityRules string `json:"admin_security_rules"` + Secgroups []*SecgroupJsonDesc `json:"secgroups"` + SecurityRules string `json:"security_rules"` + AdminSecurityRules string `json:"admin_security_rules"` + NicSecgroups []*GuestnetworkSecgroupDesc `json:"nic_secgroups"` ExtraOptions jsonutils.JSONObject `json:"extra_options"` diff --git a/pkg/apis/compute/secgroup.go b/pkg/apis/compute/secgroup.go index 687ea53604..da8f49ab90 100644 --- a/pkg/apis/compute/secgroup.go +++ b/pkg/apis/compute/secgroup.go @@ -272,6 +272,9 @@ type SecgroupDetails struct { // 关联云主机数量, 不包含回收站云主机 GuestCnt int `json:"guest_cnt,allowempty"` + // 关联云主机网卡数量, 不包含回收站云主机 + GuestNicCnt int `json:"guest_nic_cnt,allowempty"` + // 关联此安全组的云主机is_system为true数量, , 不包含回收站云主机 SystemGuestCnt int `json:"system_guest_cnt,allowempty"` @@ -310,6 +313,16 @@ type GuestsecgroupListInput struct { SecgroupFilterListInput } +type GuestnetworksecgroupListInput struct { + apis.ResourceBaseListInput + ServerFilterListInput + SecgroupFilterListInput + + NetworkIndex *int `json:"network_index"` + + IsAdmin bool `json:"is_admin"` +} + type ElasticcachesecgroupListInput struct { ElasticcacheJointsListInput SecgroupFilterListInput @@ -324,6 +337,34 @@ type GuestsecgroupDetails struct { Secgroup string `json:"secgroup"` } +type GuestnetworksecgroupDetails struct { + GuestResourceInfo + + SGuestsecgroup + + SecurityGroupResourceInfo + + ProjectId string `json:"tenant_id"` + + apis.ProjectizedResourceInfo + + // 安全组状态 + SecgroupStatus string `json:"secgroup_status"` + + // VPC ID + VpcId string `json:"vpc_id"` + Vpc string `json:"vpc"` + + NetworkIndex int `json:"network_index"` + GuestNetwork string `json:"guest_network"` + MacAddr string `json:"mac_addr"` + Ifname string `json:"ifname"` + IpAddr string `json:"ip_addr"` + Ip6Addr string `json:"ip_6_addr"` + + Admin bool `json:"admin"` +} + type ElasticcachesecgroupDetails struct { ElasticcacheJointResourceDetails @@ -353,11 +394,12 @@ type SSecurityGroupRef struct { RdsCnt int `json:"rds_cnt"` RedisCnt int `json:"redis_cnt"` LoadbalancerCnt int `json:"loadbalancer_cnt"` + GuestNicCnt int `json:"guest_nic_cnt"` TotalCnt int `json:"total_cnt"` } func (self *SSecurityGroupRef) Sum() { - self.TotalCnt = self.GuestCnt + self.AdminGuestCnt + self.RdsCnt + self.RedisCnt + self.LoadbalancerCnt + self.TotalCnt = self.GuestCnt + self.AdminGuestCnt + self.RdsCnt + self.RedisCnt + self.LoadbalancerCnt + self.GuestNicCnt } type SecurityGroupSyncstatusInput struct { diff --git a/pkg/cloudcommon/cmdline/parser.go b/pkg/cloudcommon/cmdline/parser.go index 6ccaf95e84..7cb2062109 100644 --- a/pkg/cloudcommon/cmdline/parser.go +++ b/pkg/cloudcommon/cmdline/parser.go @@ -328,6 +328,8 @@ func ParseNetworkConfig(desc string, idx int) (*compute.NetworkConfig, error) { } netConfig.Addresses6[i] = addr6.String() } + } else if strings.HasPrefix(p, "secgroups=") { + netConfig.Secgroups = strings.Split(p[len("secgroups="):], ",") } else if p == "require_designated_ip" { netConfig.RequireDesignatedIP = true } else if p == "random_exit" { diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 3075cc82b6..7a3e55693a 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -3185,6 +3185,13 @@ func (self *SGuest) PerformAttachnetwork( input.Nets[i].Driver = api.NETWORK_DRIVER_VFIO isolatedDevCount += 1 } + if len(input.Nets[i].Secgroups) > 0 { + secgroupIds, err := isValidSecgroups(ctx, userCred, input.Nets[i].Secgroups) + if err != nil { + return nil, err + } + input.Nets[i].Secgroups = secgroupIds + } if input.Nets[i].IsDefault { defaultGwCnt++ } @@ -3244,6 +3251,12 @@ func (self *SGuest) PerformAttachnetwork( return nil, errors.Wrap(err, "self.allocSriovNicDevice") } } + if len(input.Nets[i].Secgroups) > 0 { + err = self.SaveNetworkSecgroups(ctx, userCred, input.Nets[i].Secgroups, gns[0].Index) + if err != nil { + return nil, errors.Wrap(err, "SaveNetworkSecgroups") + } + } } // adjust default gateway diff --git a/pkg/compute/models/guest_queries.go b/pkg/compute/models/guest_queries.go index eb3cd77bb5..ab062d9c5d 100644 --- a/pkg/compute/models/guest_queries.go +++ b/pkg/compute/models/guest_queries.go @@ -206,6 +206,16 @@ func (manager *SGuestManager) FetchCustomizeColumns( } } } + if len(fields) == 0 || fields.Contains("network_secgroups") { + gnss := fetchGuestNetworkSecgroups(guestIds) + if gnss != nil { + for i := range rows { + if gns, ok := gnss[guestIds[i]]; ok { + rows[i].NetworkSecgroups = gns + } + } + } + } if len(fields) == 0 || fields.Contains("eip") || fields.Contains("eip_mode") { geips := fetchGuestEips(guestIds) if geips != nil { @@ -759,6 +769,79 @@ func fetchSecgroups(guestIds []string) map[string][]apis.StandaloneShortDesc { return ret } +func fetchGuestNetworkSecgroups(guestIds []string) map[string][]api.GuestnetworkSecgroupShortDesc { + guestnetworks := GuestnetworkManager.Query().SubQuery() + guestnetworksecgroups := GuestnetworksecgroupManager.Query().SubQuery() + secgroups := SecurityGroupManager.Query().SubQuery() + + q := guestnetworksecgroups.Query( + guestnetworksecgroups.Field("guest_id"), + guestnetworksecgroups.Field("network_index"), + guestnetworksecgroups.Field("secgroup_id"), + guestnetworks.Field("mac_addr").Label("mac"), + secgroups.Field("name").Label("secgroup_name"), + ) + q = q.Join(guestnetworks, sqlchemy.AND( + sqlchemy.Equals(guestnetworks.Field("guest_id"), guestnetworksecgroups.Field("guest_id")), + sqlchemy.Equals(guestnetworks.Field("index"), guestnetworksecgroups.Field("network_index")), + )) + q = q.Join(secgroups, sqlchemy.Equals(secgroups.Field("id"), guestnetworksecgroups.Field("secgroup_id"))) + q = q.Filter(sqlchemy.In(guestnetworksecgroups.Field("guest_id"), guestIds)) + + type sGuestNetworkSecgroupInfo struct { + GuestId string + NetworkIndex int + SecgroupId string + SecgroupName string + Mac string + } + + gnss := make([]sGuestNetworkSecgroupInfo, 0) + err := q.All(&gnss) + if err != nil && errors.Cause(err) != sql.ErrNoRows { + log.Errorf("fetchGuestNetworkSecgroups query error: %s", err) + return nil + } + + groupedSecgroups := make(map[string][]sGuestNetworkSecgroupInfo) + for i := range gnss { + groupedSecgroup, ok := groupedSecgroups[gnss[i].GuestId] + if !ok { + groupedSecgroup = make([]sGuestNetworkSecgroupInfo, 0) + } + groupedSecgroups[gnss[i].GuestId] = append(groupedSecgroup, gnss[i]) + } + ret := make(map[string][]api.GuestnetworkSecgroupShortDesc) + for guestId, secgroups := range groupedSecgroups { + networkGroupedSecgroups := make(map[int][]sGuestNetworkSecgroupInfo) + for i := range secgroups { + secgroupInfos, ok := networkGroupedSecgroups[secgroups[i].NetworkIndex] + if !ok { + secgroupInfos = make([]sGuestNetworkSecgroupInfo, 0) + } + networkGroupedSecgroups[secgroups[i].NetworkIndex] = append(secgroupInfos, secgroups[i]) + } + + guestnetworkSecgroups := make([]api.GuestnetworkSecgroupShortDesc, 0) + for networkIndex, secgroups := range networkGroupedSecgroups { + //networkSecgroupsDesc := make([]api.GuestnetworkSecgroupShortDesc, 0) + nsDesc := api.GuestnetworkSecgroupShortDesc{ + NetworkIndex: networkIndex, + Mac: secgroups[0].Mac, + } + for i := range secgroups { + nsDesc.Secgroups = append(nsDesc.Secgroups, apis.StandaloneShortDesc{ + Id: secgroups[i].SecgroupId, + Name: secgroups[i].SecgroupName, + }) + } + guestnetworkSecgroups = append(guestnetworkSecgroups, nsDesc) + } + ret[guestId] = guestnetworkSecgroups + } + return ret +} + type sEipInfo struct { IpAddr string Mode string diff --git a/pkg/compute/models/guest_secgroups.go b/pkg/compute/models/guest_secgroups.go index a89fac59b9..7ac755f839 100644 --- a/pkg/compute/models/guest_secgroups.go +++ b/pkg/compute/models/guest_secgroups.go @@ -414,3 +414,17 @@ func (guest *SGuest) RevokeAllSecgroups(ctx context.Context, userCred mcclient.T } return guest.saveDefaultSecgroupId(userCred, options.Options.GetDefaultSecurityGroupId(guest.Hypervisor), false) } + +func isValidSecgroups(ctx context.Context, userCred mcclient.TokenCredential, secgroups []string) ([]string, error) { + secGrpIds := []string{} + for _, secgroup := range secgroups { + secGrpObj, err := SecurityGroupManager.FetchByIdOrName(ctx, userCred, secgroup) + if err != nil { + return nil, httperrors.NewResourceNotFoundError("Secgroup %s not found", secgroup) + } + if !utils.IsInStringArray(secGrpObj.GetId(), secGrpIds) { + secGrpIds = append(secGrpIds, secGrpObj.GetId()) + } + } + return secGrpIds, nil +} diff --git a/pkg/compute/models/guestnetworks.go b/pkg/compute/models/guestnetworks.go index 2bd290f0b5..acaa17cc2e 100644 --- a/pkg/compute/models/guestnetworks.go +++ b/pkg/compute/models/guestnetworks.go @@ -741,6 +741,21 @@ func (gn *SGuestnetwork) getJsonDesc() *api.GuestnetworkJsonDesc { return desc } +func (gn *SGuestnetwork) getSecgroupDesc() *api.GuestnetworkSecgroupDesc { + secgroupJson, _ := GuestnetworksecgroupManager.getNetworkSecgroupJson(gn.GuestId, gn.Index) + if len(secgroupJson) == 0 { + return nil + } + guest := gn.GetGuest() + securityRules := guest.getNetworkSecurityGroupsRules(gn.Index) + return &api.GuestnetworkSecgroupDesc{ + Secgroups: secgroupJson, + SecurityRules: securityRules, + Index: gn.Index, + Mac: gn.MacAddr, + } +} + func (gn *SGuestnetwork) IsSriovWithoutOffload() bool { if gn.Driver != api.NETWORK_DRIVER_VFIO { return false diff --git a/pkg/compute/models/guestnetworksecgroups.go b/pkg/compute/models/guestnetworksecgroups.go new file mode 100644 index 0000000000..f761543f38 --- /dev/null +++ b/pkg/compute/models/guestnetworksecgroups.go @@ -0,0 +1,644 @@ +// 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" + "fmt" + "path" + "strconv" + + "gopkg.in/fatih/set.v0" + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" + "yunion.io/x/sqlchemy" + + 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/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/logclient" + "yunion.io/x/onecloud/pkg/util/stringutils2" +) + +type SGuestnetworksecgroupManager struct { + db.SResourceBaseManager + SGuestResourceBaseManager + SSecurityGroupResourceBaseManager + SCloudregionResourceBaseManager + SManagedResourceBaseManager + SVpcResourceBaseManager +} + +var GuestnetworksecgroupManager *SGuestnetworksecgroupManager + +func init() { + db.InitManager(func() { + GuestnetworksecgroupManager = &SGuestnetworksecgroupManager{ + SResourceBaseManager: db.NewResourceBaseManager( + SGuestnetworksecgroup{}, + "guestnetworksecgroups_tbl", + "guestnetworksecgroup", + "guestnetworksecgroups", + ), + } + GuestnetworksecgroupManager.SetVirtualObject(GuestnetworksecgroupManager) + }) +} + +type SGuestnetworksecgroup struct { + db.SResourceBase + + RowId int64 `primary:"true" auto_increment:"true" list:"user"` + + GuestId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required" index:"true"` + SSecurityGroupResourceBase `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` + NetworkIndex int `nullable:"false" list:"user" update:"user"` + + Admin bool `nullable:"false" default:"false" list:"user" create:"optional"` +} + +func (manager *SGuestnetworksecgroupManager) GetSlaveFieldName() string { + return "secgroup_id" +} + +func (self *SGuestnetworksecgroup) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { + return db.DeleteModel(ctx, userCred, self) +} + +func (manager *SGuestnetworksecgroupManager) ListItemFilter( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.GuestnetworksecgroupListInput, +) (*sqlchemy.SQuery, error) { + var err error + + q, err = manager.SGuestResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ServerFilterListInput) + if err != nil { + return nil, errors.Wrap(err, "SGuestJointsManager.ListItemFilter") + } + q, err = manager.SSecurityGroupResourceBaseManager.ListItemFilter(ctx, q, userCred, query.SecgroupFilterListInput) + if err != nil { + return nil, errors.Wrap(err, "SSecurityGroupResourceBaseManager.ListItemFilter") + } + if query.NetworkIndex != nil { + q = q.Equals("network_index", *query.NetworkIndex) + } + if query.IsAdmin { + q = q.IsFalse("admin") + } + return q, nil +} + +func (manager *SGuestnetworksecgroupManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.GuestnetworksecgroupListInput, +) (*sqlchemy.SQuery, error) { + var err error + + q, err = manager.SGuestResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ServerFilterListInput) + if err != nil { + return nil, errors.Wrap(err, "SGuestJointsManager.OrderByExtraFields") + } + q, err = manager.SSecurityGroupResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.SecgroupFilterListInput) + if err != nil { + return nil, errors.Wrap(err, "SSecurityGroupResourceBaseManager.OrderByExtraFields") + } + + return q, nil +} + +func (manager *SGuestnetworksecgroupManager) ListItemExportKeys(ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + keys stringutils2.SSortedStrings, +) (*sqlchemy.SQuery, error) { + var err error + + q, err = manager.SGuestResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SGuestJointsManager.ListItemExportKeys") + } + if keys.ContainsAny(manager.SSecurityGroupResourceBaseManager.GetExportKeys()...) { + q, err = manager.SSecurityGroupResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SSecurityGroupResourceBaseManager.ListItemExportKeys") + } + } + + return q, nil +} + +func (manager *SGuestnetworksecgroupManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []api.GuestnetworksecgroupDetails { + rows := make([]api.GuestnetworksecgroupDetails, len(objs)) + guestList := make([]interface{}, len(objs)) + guestIds := make([]string, len(objs)) + guestNetworkIdx := make([]int, len(objs)) + secgrpIds := make([]string, len(objs)) + for i := range objs { + secgrpIds[i] = objs[i].(*SGuestnetworksecgroup).SecgroupId + guestList[i] = &SGuestResourceBase{objs[i].(*SGuestnetworksecgroup).GuestId} + guestIds[i] = objs[i].(*SGuestnetworksecgroup).GuestId + guestNetworkIdx[i] = objs[i].(*SGuestnetworksecgroup).NetworkIndex + } + + groups := make(map[string]SSecurityGroup) + err := db.FetchStandaloneObjectsByIds(SecurityGroupManager, secgrpIds, groups) + if err != nil { + log.Errorf("FetchStandaloneObjectsByIds fail %s", err) + return nil + } + guestRows := manager.SGuestResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, guestList, fields, isList) + gns, err := GuestnetworkManager.fetchGuestnetworksByGuestnetworkIndex(guestIds, guestNetworkIdx) + if err != nil { + log.Errorf("failed fetchGuestnetworksByGuestnetworkIndex") + return nil + } + + regionList := make([]interface{}, len(objs)) + managerList := make([]interface{}, len(objs)) + vpcList := make([]interface{}, len(objs)) + projectList := make([]interface{}, len(objs)) + for i := range rows { + secgroupId := objs[i].(*SGuestnetworksecgroup).SecgroupId + guestId := objs[i].(*SGuestnetworksecgroup).GuestId + networkIndex := objs[i].(*SGuestnetworksecgroup).NetworkIndex + + rows[i].GuestResourceInfo = guestRows[i] + rows[i].NetworkIndex = networkIndex + rows[i].Admin = objs[i].(*SGuestnetworksecgroup).Admin + rows[i].GuestNetwork = path.Join(guestId, secgroupId, strconv.Itoa(rows[i].NetworkIndex)) + + if group, ok := groups[secgrpIds[i]]; ok { + rows[i].Secgroup = group.Name + rows[i].CloudregionId = group.CloudregionId + rows[i].ManagerId = group.ManagerId + rows[i].VpcId = group.VpcId + rows[i].SecgroupStatus = group.Status + rows[i].ProjectId = group.ProjectId + + secgroup := group + projectList[i] = &secgroup + } + regionList[i] = &SCloudregionResourceBase{rows[i].CloudregionId} + managerList[i] = &SManagedResourceBase{rows[i].ManagerId} + vpcList[i] = &SVpcResourceBase{rows[i].VpcId} + key := fmt.Sprintf("%s/%d", guestId, networkIndex) + if gn, ok := gns[key]; ok { + rows[i].MacAddr = gn.MacAddr + rows[i].IpAddr = gn.IpAddr + rows[i].Ip6Addr = gn.Ip6Addr + rows[i].Ifname = gn.Ifname + } + + } + projRows := SecurityGroupManager.SProjectizedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, projectList, fields, isList) + regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, regionList, fields, isList) + managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, managerList, fields, isList) + vpcRows := manager.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, vpcList, fields, isList) + + for i := range rows { + rows[i].ProjectizedResourceInfo = projRows[i] + rows[i].CloudregionResourceInfo = regionRows[i] + rows[i].ManagedResourceInfo = managerRows[i] + rows[i].Vpc = vpcRows[i].Vpc + } + + return rows +} + +func (manager *SGuestnetworkManager) fetchGuestnetworksByGuestnetworkIndex(guestIds []string, networkIndex []int) (map[string]SGuestnetwork, error) { + q := manager.Query() + q = q.In("guest_id", guestIds) + q = q.In("index", networkIndex) + + gns := make([]SGuestnetwork, 0) + err := q.All(&gns) + if err != nil { + return nil, err + } + res := map[string]SGuestnetwork{} + for i := range gns { + key := fmt.Sprintf("%s/%d", gns[i].GuestId, gns[i].Index) + if _, ok := res[key]; !ok { + res[key] = gns[i] + } + } + return res, nil +} + +func (manager *SGuestnetworksecgroupManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { + q, err := manager.SResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + q, err = manager.SGuestResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + q, err = manager.SSecurityGroupResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + return q, httperrors.ErrNotFound +} + +func (manager *SGuestnetworksecgroupManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) { + q, err := manager.SResourceBaseManager.QueryDistinctExtraFields(q, resource, fields) + if err == nil { + return q, nil + } + q, err = manager.SGuestResourceBaseManager.QueryDistinctExtraFields(q, resource, fields) + if err == nil { + return q, nil + } + q, err = manager.SSecurityGroupResourceBaseManager.QueryDistinctExtraFields(q, resource, fields) + if err == nil { + return q, nil + } + return q, httperrors.ErrNotFound +} + +func (manager *SGuestnetworksecgroupManager) GetGuestnetworksecgroups(guestId string, networkIndex int) ([]SSecurityGroup, error) { + q := GuestnetworksecgroupManager.Query("secgroup_id").Equals("guest_id", guestId) + if networkIndex >= 0 { + q = q.Equals("network_index", networkIndex) + } + subQ := q.SubQuery() + + secgrpQuery := SecurityGroupManager.Query() + secgrpQuery = secgrpQuery.In("id", subQ) + + secgroups := []SSecurityGroup{} + err := db.FetchModelObjects(SecurityGroupManager, secgrpQuery, &secgroups) + if err != nil { + return nil, errors.Wrapf(err, "db.FetchModelObjects") + } + return secgroups, nil +} + +// guest network attach secgroup +func (self *SGuest) PerformAddNetworkSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestNetworkAddSecgroupInput, +) (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) + } + if input.NetworkIndex == nil || *input.NetworkIndex < 0 { + return nil, httperrors.NewBadRequestError("input network index %#v is invalid", input.NetworkIndex) + } + + driver, _ := self.GetDriver() + maxCount := driver.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") + } + + guestnetwork, err := self.getGuestnetworkByIndex(*input.NetworkIndex) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "getGuestnetworkByIndex")) + } + + secgroups, err := GuestnetworksecgroupManager.GetGuestnetworksecgroups(self.Id, *input.NetworkIndex) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetGuestnetworksecgroups")) + } + if len(secgroups)+len(input.SecgroupIds) > maxCount { + return nil, httperrors.NewUnsupportOperationError("guest %s band to up to %d security groups", self.Name, maxCount) + } + + network, err := guestnetwork.GetNetwork() + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetNetwork")) + } + + vpc, err := network.GetVpc() + if err != nil { + return nil, errors.Wrap(err, "GetVpc") + } + + secgroupIds := []string{} + for _, secgroup := range secgroups { + secgroupIds = append(secgroupIds, secgroup.Id) + } + + secgroupNames := []string{} + for i := range input.SecgroupIds { + secObj, err := validators.ValidateModel(ctx, userCred, SecurityGroupManager, &input.SecgroupIds[i]) + if err != nil { + return nil, err + } + secgroup := secObj.(*SSecurityGroup) + + if utils.IsInStringArray(secObj.GetId(), secgroupIds) { + return nil, httperrors.NewInputParameterError( + "security group %s has already been assigned to guest %s network %d", + secObj.GetName(), self.GetName(), input.NetworkIndex) + } + + err = vpc.CheckSecurityGroupConsistent(secgroup) + if err != nil { + return nil, err + } + + secgroupIds = append(secgroupIds, secgroup.GetId()) + secgroupNames = append(secgroupNames, secgroup.Name) + } + + err = self.SaveNetworkSecgroups(ctx, userCred, secgroupIds, *input.NetworkIndex) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "SaveNetworkSecgroups")) + } + + 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) PerformRevokeNetworkSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestRevokeNetworkSecgroupInput, +) (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 + } + + var guestnetwork *SGuestnetwork + var err error + + if input.MacAddr != "" { + guestnetwork, err = self.GetGuestnetworkByMac(input.MacAddr) + } else if input.NetworkIndex != nil { + guestnetwork, err = self.getGuestnetworkByIndex(*input.NetworkIndex) + } else { + return nil, httperrors.NewBadRequestError("no valid network index or mac addr") + } + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "get guest network")) + } + + secgroups, err := GuestnetworksecgroupManager.GetGuestnetworksecgroups(self.Id, guestnetwork.Index) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetGuestnetworksecgroups")) + } + secgroupMaps := map[string]string{} + for _, secgroup := range secgroups { + secgroupMaps[secgroup.Id] = secgroup.Name + } + + secgroupNames := []string{} + for i := range input.SecgroupIds { + secObj, err := validators.ValidateModel(ctx, userCred, SecurityGroupManager, &input.SecgroupIds[i]) + if err != nil { + return nil, err + } + secgrp := secObj.(*SSecurityGroup) + _, ok := secgroupMaps[secgrp.GetId()] + if !ok { + return nil, httperrors.NewInputParameterError("security group %s network index %d not assigned to guest %s", + secgrp.GetName(), guestnetwork.Index, self.GetName()) + } + delete(secgroupMaps, secgrp.GetId()) + secgroupNames = append(secgroupNames, secgrp.GetName()) + } + + secgrpIds := []string{} + for secgroupId := range secgroupMaps { + secgrpIds = append(secgrpIds, secgroupId) + } + + err = self.SaveNetworkSecgroups(ctx, userCred, secgrpIds, guestnetwork.Index) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "SaveNetworkSecgroups")) + } + + 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) PerformSetNetworkSecgroup( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + input api.GuestSetNetworkSecgroupInput, +) (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") + } + + driver, _ := self.GetDriver() + maxCount := driver.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) + } + + var guestnetwork *SGuestnetwork + var err error + + if input.MacAddr != "" { + guestnetwork, err = self.GetGuestnetworkByMac(input.MacAddr) + } else if input.NetworkIndex != nil { + guestnetwork, err = self.getGuestnetworkByIndex(*input.NetworkIndex) + } else { + return nil, httperrors.NewBadRequestError("no valid network index or mac addr") + } + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "get guest network")) + } + + network, err := guestnetwork.GetNetwork() + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrap(err, "GetNetwork")) + } + + vpc, err := network.GetVpc() + if err != nil { + return nil, errors.Wrap(err, "GetVpc") + } + + secgroupIds := []string{} + secgroupNames := []string{} + for i := range input.SecgroupIds { + secObj, err := validators.ValidateModel(ctx, userCred, SecurityGroupManager, &input.SecgroupIds[i]) + if err != nil { + return nil, err + } + secgrp := secObj.(*SSecurityGroup) + + err = vpc.CheckSecurityGroupConsistent(secgrp) + if err != nil { + return nil, err + } + + if !utils.IsInStringArray(secgrp.GetId(), secgroupIds) { + secgroupIds = append(secgroupIds, secgrp.GetId()) + secgroupNames = append(secgroupNames, secgrp.GetName()) + } + } + + err = self.SaveNetworkSecgroups(ctx, userCred, secgroupIds, guestnetwork.Index) + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "SaveNetworkSecgroups")) + } + + 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) SaveNetworkSecgroups( + ctx context.Context, userCred mcclient.TokenCredential, secgroupIds []string, networkIndex int, +) error { + if len(secgroupIds) == 0 { + return self.RevokeNetworkAllSecgroups(ctx, userCred, networkIndex) + } + oldIds := set.New(set.ThreadSafe) + newIds := set.New(set.ThreadSafe) + gnss, err := self.GetGuestNetworkSecgroups(networkIndex) + if err != nil { + return errors.Wrap(err, "GetGuestNetworkSecgroups") + } + secgroupMaps := map[string]SGuestnetworksecgroup{} + for i := range gnss { + oldIds.Add(gnss[i].SecgroupId) + secgroupMaps[gnss[i].SecgroupId] = gnss[i] + } + for i := range secgroupIds { + newIds.Add(secgroupIds[i]) + } + for _, removed := range set.Difference(oldIds, newIds).List() { + id := removed.(string) + gns, ok := secgroupMaps[id] + if ok { + err = gns.Delete(ctx, userCred) + if err != nil { + return errors.Wrapf(err, + "Delete guest network secgroup for guest %s network index %d secgroup %s", + self.GetName(), networkIndex, id) + } + } + } + for _, added := range set.Difference(newIds, oldIds).List() { + id := added.(string) + err = self.newGuestNetworkSecgroup(ctx, id, networkIndex, false) + if err != nil { + return errors.Wrapf(err, + "New guest network secgroup for guest %s network index %d with secgroup %s", + self.GetName(), networkIndex, id) + } + } + return nil +} + +func (self *SGuest) newGuestNetworkSecgroup(ctx context.Context, secgroupId string, networkIndex int, isAdmin bool) error { + gns := &SGuestnetworksecgroup{} + gns.SetModelManager(GuestnetworksecgroupManager, gns) + gns.GuestId = self.Id + gns.SecgroupId = secgroupId + gns.NetworkIndex = networkIndex + gns.Admin = isAdmin + return GuestnetworksecgroupManager.TableSpec().Insert(ctx, gns) +} + +func (self *SGuest) GetGuestNetworkSecgroups(networkIndex int) ([]SGuestnetworksecgroup, error) { + gss := []SGuestnetworksecgroup{} + q := GuestnetworksecgroupManager.Query().Equals("guest_id", self.Id).Equals("network_index", networkIndex) + err := db.FetchModelObjects(GuestnetworksecgroupManager, q, &gss) + if err != nil { + return nil, errors.Wrapf(err, "db.FetchModelObjects") + } + return gss, nil +} + +func (self *SGuest) RevokeNetworkAllSecgroups(ctx context.Context, userCred mcclient.TokenCredential, networkIndex int) error { + gss, err := self.GetGuestNetworkSecgroups(networkIndex) + if err != nil { + return errors.Wrapf(err, "GetGuestNetworkSecgroups") + } + for i := range gss { + err = gss[i].Delete(ctx, userCred) + if err != nil { + return errors.Wrap(err, "Delete") + } + } + + //return self.newGuestNetworkSecgroup(ctx, options.Options.DefaultSecurityGroupId, networkIndex, false) + return nil +} + +func (self *SGuestnetworksecgroupManager) getNetworkSecgroupJson(guestId string, networkIndex int) ([]*api.SecgroupJsonDesc, error) { + ret := []*api.SecgroupJsonDesc{} + secgroups, err := GuestnetworksecgroupManager.GetGuestnetworksecgroups(guestId, networkIndex) + if err != nil { + return nil, errors.Wrap(err, "GetSecgroups") + } + for _, secGrp := range secgroups { + ret = append(ret, secGrp.getDesc()) + } + return ret, nil +} + +func (self *SGuest) RevokeAllNetworkSecgroups(ctx context.Context, userCred mcclient.TokenCredential) error { + gns, err := self.GetNetworks("") + if err != nil { + return errors.Wrap(err, "GetNetworks") + } + for i := range gns { + gnss, err := self.GetGuestNetworkSecgroups(gns[i].Index) + if err != nil { + return errors.Wrap(err, "GetGuestNetworkSecgroups") + } + for j := range gnss { + err = gnss[j].Delete(ctx, userCred) + if err != nil { + return errors.Wrap(err, "Delete guestnetworksecgroup") + } + } + } + return nil +} diff --git a/pkg/compute/models/guestresource.go b/pkg/compute/models/guestresource.go index d1e3e049ef..b4a18a79bb 100644 --- a/pkg/compute/models/guestresource.go +++ b/pkg/compute/models/guestresource.go @@ -32,7 +32,7 @@ import ( ) type SGuestResourceBase struct { - GuestId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` + GuestId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional" json:"guest_id"` } type SGuestResourceBaseManager struct { @@ -98,7 +98,7 @@ func (manager *SGuestResourceBaseManager) FetchCustomizeColumns( var base *SGuestResourceBase err := reflectutils.FindAnonymouStructPointer(objs[i], &base) if err != nil { - log.Errorf("Cannot find SGuestResourceBase in object %s", objs[i]) + log.Errorf("Cannot find SGuestResourceBase in object %s %s", objs[i], err) continue } guestIds[i] = base.GuestId diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index fdedc88c03..50401816e3 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -1222,6 +1222,18 @@ func (guest *SGuest) ConvertEsxiNetworks(targetGuest *SGuest) error { return err } +func (guest *SGuest) getGuestnetworkByIndex(networkIndex int) (*SGuestnetwork, error) { + q := guest.GetNetworksQuery("").Equals("index", networkIndex) + + guestnic := SGuestnetwork{} + err := q.First(&guestnic) + if err != nil { + return nil, err + } + guestnic.SetModelManager(GuestnetworkManager, &guestnic) + return &guestnic, nil +} + func (guest *SGuest) getGuestnetworkByIpOrMac(ipAddr string, ip6Addr string, macAddr string) (*SGuestnetwork, error) { q := guest.GetNetworksQuery("") if len(ipAddr) > 0 { @@ -2139,6 +2151,11 @@ func (manager *SGuestManager) validateCreateData( netConfig.SriovDevice = devConfig netConfig.Driver = api.NETWORK_DRIVER_VFIO } + secgroupIds, err := isValidSecgroups(ctx, userCred, netConfig.Secgroups) + if err != nil { + return nil, err + } + netConfig.Secgroups = secgroupIds netConfig.Project = ownerId.GetProjectId() netConfig.Domain = ownerId.GetProjectDomainId() @@ -2205,15 +2222,9 @@ func (manager *SGuestManager) validateCreateData( input.KeypairId = keypairObj.GetId() } - secGrpIds := []string{} - for _, secgroup := range input.Secgroups { - secGrpObj, err := SecurityGroupManager.FetchByIdOrName(ctx, userCred, secgroup) - if err != nil { - return nil, httperrors.NewResourceNotFoundError("Secgroup %s not found", secgroup) - } - if !utils.IsInStringArray(secGrpObj.GetId(), secGrpIds) { - secGrpIds = append(secGrpIds, secGrpObj.GetId()) - } + secGrpIds, err := isValidSecgroups(ctx, userCred, input.Secgroups) + if err != nil { + return nil, err } if len(secGrpIds) > 0 { input.SecgroupId = secGrpIds[0] @@ -3190,6 +3201,26 @@ func (self *SGuest) getSecurityGroupsRules() string { return strings.Join(rules, SECURITY_GROUP_SEPARATOR) } +func (self *SGuest) getNetworkSecurityGroupsRules(networkIndex int) string { + gnss, _ := self.GetGuestNetworkSecgroups(networkIndex) + secgroupids := []string{} + for _, gns := range gnss { + secgroupids = append(secgroupids, gns.SecgroupId) + } + q := SecurityGroupRuleManager.Query() + q.Filter(sqlchemy.In(q.Field("secgroup_id"), secgroupids)).Desc(q.Field("priority"), q.Field("action")) + secrules := []SSecurityGroupRule{} + if err := db.FetchModelObjects(SecurityGroupRuleManager, q, &secrules); err != nil { + log.Errorf("Get security group rules error: %v", err) + return "" + } + rules := []string{} + for _, rule := range secrules { + rules = append(rules, rule.String()) + } + return strings.Join(rules, SECURITY_GROUP_SEPARATOR) +} + func (self *SGuest) getAdminSecurityRules() string { secgrp := self.getAdminSecgroup() if secgrp != nil { @@ -4516,7 +4547,12 @@ func (self *SGuest) CreateNetworksOnHost( return errors.Wrap(err, "self.allocSriovNicDevice") } } - + if len(netConfig.Secgroups) > 0 { + err = self.SaveNetworkSecgroups(ctx, userCred, netConfig.Secgroups, gns[0].Index) + if err != nil { + return errors.Wrap(err, "SaveNetworkSecgroups") + } + } } return nil } @@ -5472,6 +5508,13 @@ func (self *SGuest) GetJsonDescAtHypervisor(ctx context.Context, host *SHost) *a if len(nicDesc.Domain) > 0 { desc.Domain = nicDesc.Domain } + secgroupDesc := nic.getSecgroupDesc() + if secgroupDesc != nil { + if desc.NicSecgroups == nil { + desc.NicSecgroups = make([]*api.GuestnetworkSecgroupDesc, 0) + } + desc.NicSecgroups = append(desc.NicSecgroups, secgroupDesc) + } } { diff --git a/pkg/compute/models/secgroupresource.go b/pkg/compute/models/secgroupresource.go index c3f8beb99a..4a70486bbb 100644 --- a/pkg/compute/models/secgroupresource.go +++ b/pkg/compute/models/secgroupresource.go @@ -104,6 +104,7 @@ func (manager *SSecurityGroupResourceBaseManager) FetchCustomizeColumns( regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, regionList, fields, isList) managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, managerList, fields, isList) + for i := range rows { rows[i].CloudregionResourceInfo = regionRows[i] rows[i].ManagedResourceInfo = managerRows[i] diff --git a/pkg/compute/models/secgroups.go b/pkg/compute/models/secgroups.go index bb8681a910..db759e64aa 100644 --- a/pkg/compute/models/secgroups.go +++ b/pkg/compute/models/secgroups.go @@ -410,6 +410,7 @@ func (manager *SSecurityGroupManager) FetchCustomizeColumns( adminGuestMaps := map[string]int{} systemGuestMaps := map[string]int{} normalGuestMaps := map[string]int{} + guestNetworkMaps := map[string]int{} for i := range guests { if guests[i].IsSystem { if _, ok := systemGuestMaps[guests[i].SecgrpId]; !ok { @@ -456,6 +457,22 @@ func (manager *SSecurityGroupManager) FetchCustomizeColumns( return rows } + gnq := GuestnetworksecgroupManager.Query() + gq := GuestManager.Query().IsFalse("pending_deleted").SubQuery() + gnq = gnq.Join(gq, sqlchemy.Equals(gnq.Field("guest_id"), gq.Field("id"))) + guestNetworkSecgroups := []SGuestnetworksecgroup{} + err = db.FetchModelObjects(GuestnetworksecgroupManager, gnq, &guestNetworkSecgroups) + if err != nil { + log.Errorf("db.FetchModelObjects error: %v", err) + return rows + } + for i := range guestNetworkSecgroups { + if _, ok := guestNetworkMaps[guestNetworkSecgroups[i].SecgroupId]; !ok { + guestNetworkMaps[guestNetworkSecgroups[i].SecgroupId] = 0 + } + guestNetworkMaps[guestNetworkSecgroups[i].SecgroupId]++ + } + totalCnt, err := manager.TotalCnt(secgroupIds) if err != nil { return rows @@ -464,6 +481,7 @@ func (manager *SSecurityGroupManager) FetchCustomizeColumns( rows[i].GuestCnt, _ = normalGuestMaps[secgroupIds[i]] rows[i].AdminGuestCnt, _ = adminGuestMaps[secgroupIds[i]] rows[i].SystemGuestCnt, _ = systemGuestMaps[secgroupIds[i]] + rows[i].GuestNicCnt, _ = guestNetworkMaps[secgroupIds[i]] if cnt, ok := totalCnt[secgroupIds[i]]; ok { rows[i].TotalCnt = cnt.TotalCnt rows[i].LoadbalancerCnt = cnt.LoadbalancerCnt @@ -1003,6 +1021,7 @@ func (sm *SSecurityGroupManager) TotalCnt(secIds []string) (map[string]api.SSecu g1SQ := sm.query(GuestsecgroupManager, "secgroup_id", "guest1", secIds) g2SQ := sm.query(GuestManager, "secgrp_id", "guest2", secIds) g3SQ := sm.query(GuestManager, "admin_secgrp_id", "guest3", secIds) + g4SQ := sm.query(GuestnetworksecgroupManager, "secgroup_id", "guest4", secIds) rdsSQ := sm.query(DBInstanceSecgroupManager, "secgroup_id", "rds", secIds) redisSQ := sm.query(ElasticcachesecgroupManager, "secgroup_id", "redis", secIds) @@ -1016,6 +1035,7 @@ func (sm *SSecurityGroupManager) TotalCnt(secIds []string) (map[string]api.SSecu sqlchemy.SUM("rds_cnt", rdsSQ.Field("rds")), sqlchemy.SUM("redis_cnt", redisSQ.Field("redis")), sqlchemy.SUM("loadbalancer_cnt", lbSQ.Field("loadbalancer")), + sqlchemy.SUM("guest_nic_cnt", g4SQ.Field("guest4")), ) secQ.AppendField(secQ.Field("id")) @@ -1026,6 +1046,7 @@ func (sm *SSecurityGroupManager) TotalCnt(secIds []string) (map[string]api.SSecu secQ = secQ.LeftJoin(rdsSQ, sqlchemy.Equals(secQ.Field("id"), rdsSQ.Field("secgroup_id"))) secQ = secQ.LeftJoin(redisSQ, sqlchemy.Equals(secQ.Field("id"), redisSQ.Field("secgroup_id"))) secQ = secQ.LeftJoin(lbSQ, sqlchemy.Equals(secQ.Field("id"), lbSQ.Field("secgroup_id"))) + secQ = secQ.LeftJoin(g4SQ, sqlchemy.Equals(secQ.Field("id"), g4SQ.Field("secgroup_id"))) secQ = secQ.Filter(sqlchemy.In(secQ.Field("id"), secIds)).GroupBy(secQ.Field("id")) diff --git a/pkg/compute/service/handlers.go b/pkg/compute/service/handlers.go index 1d512dfd6b..2bf182ba4b 100644 --- a/pkg/compute/service/handlers.go +++ b/pkg/compute/service/handlers.go @@ -170,6 +170,7 @@ func InitHandlers(app *appsrv.Application, isSlave bool) { models.InterVpcNetworkRouteSetManager, models.GuestScreenDumpManager, models.BillingResourceCheckManager, + models.GuestnetworksecgroupManager, models.SchedpolicyManager, models.DynamicschedtagManager, diff --git a/pkg/compute/tasks/guest/guest_delete_task.go b/pkg/compute/tasks/guest/guest_delete_task.go index 05e37de410..5050a30d8c 100644 --- a/pkg/compute/tasks/guest/guest_delete_task.go +++ b/pkg/compute/tasks/guest/guest_delete_task.go @@ -340,6 +340,7 @@ func (deleteTask *BaseGuestDeleteTask) OnGuestDeleteCompleteFailed(ctx context.C func (deleteTask *BaseGuestDeleteTask) OnGuestDeleteComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { guest := obj.(*models.SGuest) + guest.RevokeAllNetworkSecgroups(ctx, deleteTask.UserCred) guest.DetachAllNetworks(ctx, deleteTask.UserCred) guest.EjectAllIso(deleteTask.UserCred) guest.EjectAllVfd(deleteTask.UserCred) diff --git a/pkg/hostman/guestman/desc/desc.go b/pkg/hostman/guestman/desc/desc.go index a10d28b99f..56293ea597 100644 --- a/pkg/hostman/guestman/desc/desc.go +++ b/pkg/hostman/guestman/desc/desc.go @@ -379,11 +379,13 @@ type SGuestControlDesc struct { // is volatile host meaning guest not running on this host right now IsVolatileHost bool - ScalingGroupId string + ScalingGroupId string + SrcIpCheck bool + SrcMacCheck bool + SecurityRules string AdminSecurityRules string - SrcIpCheck bool - SrcMacCheck bool + NicSecgroups []*api.GuestnetworkSecgroupDesc `json:"nic_secgroups,omitempty"` EncryptKeyId string diff --git a/pkg/mcclient/modules/compute/mod_servernetworksecgroups.go b/pkg/mcclient/modules/compute/mod_servernetworksecgroups.go new file mode 100644 index 0000000000..4239b2ad47 --- /dev/null +++ b/pkg/mcclient/modules/compute/mod_servernetworksecgroups.go @@ -0,0 +1,33 @@ +// 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 compute + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modulebase" + "yunion.io/x/onecloud/pkg/mcclient/modules" +) + +var ( + Servernetworksecgroups modulebase.ResourceManager +) + +func init() { + Servernetworksecgroups = modules.NewComputeManager( + "guestnetworksecgroup", + "guestnetworksecgroups", + []string{"Guest_id", "Network_index", "Secgroup_id", "Admin"}, + []string{}) + modules.RegisterCompute(&Servernetworksecgroups) +} diff --git a/pkg/mcclient/options/compute/secgroup.go b/pkg/mcclient/options/compute/secgroup.go index c4eb2cfd2a..6bb3824158 100644 --- a/pkg/mcclient/options/compute/secgroup.go +++ b/pkg/mcclient/options/compute/secgroup.go @@ -197,3 +197,16 @@ type SecgroupCleanOptions struct { func (opts *SecgroupCleanOptions) Params() (jsonutils.JSONObject, error) { return nil, nil } + +type ServerNetworkSecgroupListOptions struct { + baseoptions.BaseListOptions + + Server string `help:"Server Id or name"` + Secgroup string `help:"Secgroup Id or name"` + NetworkIndex *int `help:"Server network index"` + IsAdmin bool `help:"Is admin secgroup"` +} + +func (opts *ServerNetworkSecgroupListOptions) Params() (jsonutils.JSONObject, error) { + return baseoptions.ListStructToParams(opts) +} diff --git a/pkg/mcclient/options/compute/servers.go b/pkg/mcclient/options/compute/servers.go index 2a1a4cdd58..0501ec0bed 100644 --- a/pkg/mcclient/options/compute/servers.go +++ b/pkg/mcclient/options/compute/servers.go @@ -838,6 +838,20 @@ func (opts *ServerSecGroupsOptions) Params() (jsonutils.JSONObject, error) { return jsonutils.Marshal(map[string][]string{"secgroup_ids": opts.SecgroupIds}), nil } +type ServerNetworkSecGroupsOptions struct { + ID string `help:"ID or Name of server" metavar:"Guest" json:"-"` + NetworkIndex *int `help:"Guest network index" metavar:"Network Index"` + SecgroupIds []string `help:"Ids of Security Groups" metavar:"Security Groups" positional:"true"` +} + +func (o *ServerNetworkSecGroupsOptions) GetId() string { + return o.ID +} + +func (opts *ServerNetworkSecGroupsOptions) Params() (jsonutils.JSONObject, error) { + return jsonutils.Marshal(opts), nil +} + type ServerModifySrcCheckOptions struct { ID string `help:"ID or Name of server" metavar:"Guest" json:"-"` SrcIpCheck string `help:"Turn on/off src ip check" choices:"on|off"` diff --git a/pkg/vpcagent/models/models.go b/pkg/vpcagent/models/models.go index cabc3a2fe5..989ac9b789 100644 --- a/pkg/vpcagent/models/models.go +++ b/pkg/vpcagent/models/models.go @@ -88,6 +88,9 @@ type Guestnetwork struct { Network *Network `json:"-"` Elasticip *Elasticip `json:"-"` SubIPs NetworkAddresses `json:"-"` + + // guest nic level secgroup + Guestnetworksecgroups Guestnetworksecgroups `json:"-"` } func (el *Guestnetwork) Copy() *Guestnetwork { @@ -109,6 +112,22 @@ func (el *NetworkAddress) Copy() *NetworkAddress { } } +type Guestnetworksecgroup struct { + compute_models.SGuestnetworksecgroup + + SecurityGroup *SecurityGroup `json:"-"` +} + +func (el *Guestnetworksecgroup) ModelSetKey() string { + return fmt.Sprintf("%s/%d", el.GuestId, el.NetworkIndex) +} + +func (el *Guestnetworksecgroup) Copy() *Guestnetworksecgroup { + return &Guestnetworksecgroup{ + SGuestnetworksecgroup: el.SGuestnetworksecgroup, + } +} + type Guest struct { compute_models.SGuest diff --git a/pkg/vpcagent/models/modelset.go b/pkg/vpcagent/models/modelset.go index 158627ebf4..55c1872a97 100644 --- a/pkg/vpcagent/models/modelset.go +++ b/pkg/vpcagent/models/modelset.go @@ -39,8 +39,9 @@ type ( Elasticips map[string]*Elasticip NetworkAddresses map[string]*NetworkAddress - Guestnetworks map[string]*Guestnetwork // key: rowId - Guestsecgroups map[string]*Guestsecgroup // key: guestId/secgroupId + Guestnetworks map[string]*Guestnetwork // key: rowId + Guestsecgroups map[string]*Guestsecgroup // key: guestId/secgroupId + Guestnetworksecgroups map[string]*Guestnetworksecgroup // key: guestId/networkIndex DnsZones map[string]*DnsZone DnsRecords map[string]*DnsRecord @@ -583,6 +584,59 @@ func (set Guestnetworks) joinNetworkAddresses(subEntries NetworkAddresses) bool return correct } +func (set Guestnetworks) joinGuestnetworksecgroups(subEntries Guestnetworksecgroups) bool { + for _, gn := range set { + key := fmt.Sprintf("%s/%d", gn.GuestId, gn.Index) + gns, ok := subEntries[key] + if !ok { + continue + } + if len(gn.Guestnetworksecgroups) == 0 { + gn.Guestnetworksecgroups = Guestnetworksecgroups{} + } + gn.Guestnetworksecgroups[key] = gns + } + return true +} + +func (set Guestnetworksecgroups) ModelManager() mcclient_modulebase.IBaseManager { + return &mcclient_modules.Servernetworksecgroups +} + +func (set Guestnetworksecgroups) DBModelManager() db.IModelManager { + return models.GuestnetworksecgroupManager +} + +func (set Guestnetworksecgroups) NewModel() db.IModel { + return &Guestnetworksecgroup{} +} + +func (set Guestnetworksecgroups) AddModel(i db.IModel) { + m := i.(*Guestnetworksecgroup) + k := m.ModelSetKey() + set[k] = m +} + +func (set Guestnetworksecgroups) Copy() apihelper.IModelSet { + setCopy := Guestnetworksecgroups{} + for id, el := range set { + setCopy[id] = el.Copy() + } + return setCopy +} + +func (set Guestnetworksecgroups) joinSecurityGroups(subEntries SecurityGroups) bool { + for _, gns := range set { + key := gns.SecgroupId + secgroup, ok := subEntries[key] + if !ok { + continue + } + gns.SecurityGroup = secgroup + } + return true +} + func (set NetworkAddresses) ModelManager() mcclient_modulebase.IBaseManager { return &mcclient_modules.NetworkAddresses } diff --git a/pkg/vpcagent/models/modelsets.go b/pkg/vpcagent/models/modelsets.go index 6cee1e972c..2f9dbe54ec 100644 --- a/pkg/vpcagent/models/modelsets.go +++ b/pkg/vpcagent/models/modelsets.go @@ -27,17 +27,18 @@ import ( ) type ModelSetsMaxUpdatedAt struct { - Vpcs time.Time - Wires time.Time - Networks time.Time - Guests time.Time - Hosts time.Time - SecurityGroups time.Time - SecurityGroupRules time.Time - Guestnetworks time.Time - Guestsecgroups time.Time - Elasticips time.Time - NetworkAddresses time.Time + Vpcs time.Time + Wires time.Time + Networks time.Time + Guests time.Time + Hosts time.Time + SecurityGroups time.Time + SecurityGroupRules time.Time + Guestnetworks time.Time + Guestsecgroups time.Time + Elasticips time.Time + NetworkAddresses time.Time + Guestnetworksecgroups time.Time DnsZones time.Time DnsRecords time.Time @@ -54,17 +55,18 @@ type ModelSetsMaxUpdatedAt struct { func NewModelSetsMaxUpdatedAt() *ModelSetsMaxUpdatedAt { return &ModelSetsMaxUpdatedAt{ - Vpcs: apihelper.PseudoZeroTime, - Wires: apihelper.PseudoZeroTime, - Networks: apihelper.PseudoZeroTime, - Guests: apihelper.PseudoZeroTime, - Hosts: apihelper.PseudoZeroTime, - SecurityGroups: apihelper.PseudoZeroTime, - SecurityGroupRules: apihelper.PseudoZeroTime, - Guestnetworks: apihelper.PseudoZeroTime, - Guestsecgroups: apihelper.PseudoZeroTime, - Elasticips: apihelper.PseudoZeroTime, - NetworkAddresses: apihelper.PseudoZeroTime, + Vpcs: apihelper.PseudoZeroTime, + Wires: apihelper.PseudoZeroTime, + Networks: apihelper.PseudoZeroTime, + Guests: apihelper.PseudoZeroTime, + Hosts: apihelper.PseudoZeroTime, + SecurityGroups: apihelper.PseudoZeroTime, + SecurityGroupRules: apihelper.PseudoZeroTime, + Guestnetworks: apihelper.PseudoZeroTime, + Guestsecgroups: apihelper.PseudoZeroTime, + Elasticips: apihelper.PseudoZeroTime, + NetworkAddresses: apihelper.PseudoZeroTime, + Guestnetworksecgroups: apihelper.PseudoZeroTime, DnsZones: apihelper.PseudoZeroTime, DnsRecords: apihelper.PseudoZeroTime, @@ -81,17 +83,18 @@ func NewModelSetsMaxUpdatedAt() *ModelSetsMaxUpdatedAt { } type ModelSets struct { - Vpcs Vpcs - Wires Wires - Networks Networks - Guests Guests - Hosts Hosts - SecurityGroups SecurityGroups - SecurityGroupRules SecurityGroupRules - Guestnetworks Guestnetworks - Guestsecgroups Guestsecgroups - Elasticips Elasticips - NetworkAddresses NetworkAddresses + Vpcs Vpcs + Wires Wires + Networks Networks + Guests Guests + Hosts Hosts + SecurityGroups SecurityGroups + SecurityGroupRules SecurityGroupRules + Guestnetworks Guestnetworks + Guestsecgroups Guestsecgroups + Elasticips Elasticips + NetworkAddresses NetworkAddresses + Guestnetworksecgroups Guestnetworksecgroups DnsZones DnsZones DnsRecords DnsRecords @@ -109,17 +112,18 @@ type ModelSets struct { func NewModelSets() *ModelSets { return &ModelSets{ - Vpcs: Vpcs{}, - Wires: Wires{}, - Networks: Networks{}, - Guests: Guests{}, - Hosts: Hosts{}, - SecurityGroups: SecurityGroups{}, - SecurityGroupRules: SecurityGroupRules{}, - Guestnetworks: Guestnetworks{}, - Guestsecgroups: Guestsecgroups{}, - Elasticips: Elasticips{}, - NetworkAddresses: NetworkAddresses{}, + Vpcs: Vpcs{}, + Wires: Wires{}, + Networks: Networks{}, + Guests: Guests{}, + Hosts: Hosts{}, + SecurityGroups: SecurityGroups{}, + SecurityGroupRules: SecurityGroupRules{}, + Guestnetworks: Guestnetworks{}, + Guestsecgroups: Guestsecgroups{}, + Elasticips: Elasticips{}, + NetworkAddresses: NetworkAddresses{}, + Guestnetworksecgroups: Guestnetworksecgroups{}, DnsZones: DnsZones{}, DnsRecords: DnsRecords{}, @@ -150,6 +154,7 @@ func (mss *ModelSets) ModelSetList() []apihelper.IModelSet { mss.Guestsecgroups, mss.Elasticips, mss.NetworkAddresses, + mss.Guestnetworksecgroups, mss.DnsZones, mss.DnsRecords, @@ -172,17 +177,18 @@ func (mss *ModelSets) NewEmpty() apihelper.IModelSets { func (mss *ModelSets) copy_() *ModelSets { mssCopy := &ModelSets{ - Vpcs: mss.Vpcs.Copy().(Vpcs), - Wires: mss.Wires.Copy().(Wires), - Networks: mss.Networks.Copy().(Networks), - Guests: mss.Guests.Copy().(Guests), - Hosts: mss.Hosts.Copy().(Hosts), - SecurityGroups: mss.SecurityGroups.Copy().(SecurityGroups), - SecurityGroupRules: mss.SecurityGroupRules.Copy().(SecurityGroupRules), - Guestnetworks: mss.Guestnetworks.Copy().(Guestnetworks), - Guestsecgroups: mss.Guestsecgroups.Copy().(Guestsecgroups), - Elasticips: mss.Elasticips.Copy().(Elasticips), - NetworkAddresses: mss.NetworkAddresses.Copy().(NetworkAddresses), + Vpcs: mss.Vpcs.Copy().(Vpcs), + Wires: mss.Wires.Copy().(Wires), + Networks: mss.Networks.Copy().(Networks), + Guests: mss.Guests.Copy().(Guests), + Hosts: mss.Hosts.Copy().(Hosts), + SecurityGroups: mss.SecurityGroups.Copy().(SecurityGroups), + SecurityGroupRules: mss.SecurityGroupRules.Copy().(SecurityGroupRules), + Guestnetworks: mss.Guestnetworks.Copy().(Guestnetworks), + Guestsecgroups: mss.Guestsecgroups.Copy().(Guestsecgroups), + Elasticips: mss.Elasticips.Copy().(Elasticips), + NetworkAddresses: mss.NetworkAddresses.Copy().(NetworkAddresses), + Guestnetworksecgroups: mss.Guestnetworksecgroups.Copy().(Guestnetworksecgroups), DnsZones: mss.DnsZones.Copy().(DnsZones), DnsRecords: mss.DnsRecords.Copy().(DnsRecords), @@ -273,12 +279,16 @@ func (mss *ModelSets) join() bool { msg = append(msg, "mss.SecurityGroups.joinSecurityGroupRules(mss.SecurityGroupRules)") p = append(p, mss.Guestsecgroups.join(mss.SecurityGroups, mss.Guests)) msg = append(msg, "mss.Guestsecgroups.join(mss.SecurityGroups, mss.Guests)") + p = append(p, mss.Guestnetworksecgroups.joinSecurityGroups(mss.SecurityGroups)) + msg = append(msg, "mss.Guestnetworksecgroups.joinSecurityGroups(mss.SecurityGroups)") p = append(p, mss.Guestnetworks.joinGuests(mss.Guests)) msg = append(msg, "mss.Guestnetworks.joinGuests(mss.Guests)") p = append(p, mss.Guestnetworks.joinElasticips(mss.Elasticips)) msg = append(msg, "mss.Guestnetworks.joinElasticips(mss.Elasticips)") p = append(p, mss.Guestnetworks.joinNetworkAddresses(mss.NetworkAddresses)) msg = append(msg, "mss.Guestnetworks.joinNetworkAddresses(mss.NetworkAddresses)") + p = append(p, mss.Guestnetworks.joinGuestnetworksecgroups(mss.Guestnetworksecgroups)) + msg = append(msg, "mss.Guestnetworks.joinGuestnetworksecgroups(mss.Guestnetworksecgroups)") p = append(p, mss.Groups.joinGroupnetworks(mss.Groupnetworks, mss.Networks)) msg = append(msg, "mss.Groups.joinGroupnetworks(mss.Groupnetworks, mss.Networks)") p = append(p, mss.Groupnetworks.joinElasticips(mss.Elasticips)) diff --git a/pkg/vpcagent/models/secgrouprules_sort.go b/pkg/vpcagent/models/secgrouprules_sort.go index ae2b9bc37f..e596fe270a 100644 --- a/pkg/vpcagent/models/secgrouprules_sort.go +++ b/pkg/vpcagent/models/secgrouprules_sort.go @@ -53,6 +53,37 @@ func (el *Guest) OrderedSecurityGroupRules() []*SecurityGroupRule { return rs } +func (el *Guestnetwork) OrderedSecurityGroupRules(guest *Guest) []*SecurityGroupRule { + if len(el.Guestnetworksecgroups) == 0 { + return guest.OrderedSecurityGroupRules() + } + // deny any incoming traffic and allow ARP + rs := []*SecurityGroupRule{ + { + // deny all in-bound traffic + SSecurityGroupRule: compute_models.SSecurityGroupRule{ + Priority: 1, + Direction: string(secrules.SecurityRuleIngress), + Action: string(secrules.SecurityRuleDeny), + }, + }, + { + // allow in-bound arp traffic + SSecurityGroupRule: compute_models.SSecurityGroupRule{ + Priority: 2, + Direction: string(secrules.SecurityRuleIngress), + Protocol: "arp", + Action: string(secrules.SecurityRuleAllow), + }, + }, + } + for _, guestSecgroup := range el.Guestnetworksecgroups { + rs = append(rs, guestSecgroup.SecurityGroup.securityGroupRules(100)...) + } + sort.Slice(rs, SecurityGroupRuleLessFunc(rs)) + return rs +} + func (el *SecurityGroup) securityGroupRules(basePriority int64) []*SecurityGroupRule { rs := make([]*SecurityGroupRule, 0, len(el.SecurityGroupRules)) for _, r := range el.SecurityGroupRules { diff --git a/pkg/vpcagent/ovn/keeper.go b/pkg/vpcagent/ovn/keeper.go index 4b0caf3a8e..e617ca8f3b 100644 --- a/pkg/vpcagent/ovn/keeper.go +++ b/pkg/vpcagent/ovn/keeper.go @@ -822,7 +822,7 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn if len(guestnetwork.Ip6Addr) > 0 { enableIPv6 = true } - sgrs := guest.OrderedSecurityGroupRules() + sgrs := guestnetwork.OrderedSecurityGroupRules(guest) for _, sgr := range sgrs { // kvm not support peer secgroup acl, err := ruleToAcl(lportName, sgr, enableIPv6)