Files
cloudpods/pkg/compute/models/secgroups.go
2020-04-03 02:03:55 +08:00

951 lines
30 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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"
"sort"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/pkg/util/secrules"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/rbacutils"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type SSecurityGroupManager struct {
db.SSharableVirtualResourceBaseManager
}
var SecurityGroupManager *SSecurityGroupManager
func init() {
SecurityGroupManager = &SSecurityGroupManager{
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
SSecurityGroup{},
"secgroups_tbl",
"secgroup",
"secgroups",
),
}
SecurityGroupManager.NameLength = 128
SecurityGroupManager.NameRequireAscii = true
SecurityGroupManager.SetVirtualObject(SecurityGroupManager)
}
const (
SECURITY_GROUP_SEPARATOR = ";"
)
type SSecurityGroup struct {
db.SSharableVirtualResourceBase
IsDirty bool `nullable:"false" default:"false"`
}
// 安全组列表
func (manager *SSecurityGroupManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
input api.SecgroupListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.SharableVirtualResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemFilter")
}
if len(input.Equals) > 0 {
_secgroup, err := manager.FetchByIdOrName(userCred, input.Equals)
if err != nil {
return nil, httperrors.NewInputParameterError("Failed fetching secgroup %s", input.Equals)
}
secgroup := _secgroup.(*SSecurityGroup)
inAllowList := secgroup.GetInAllowList()
outAllowList := secgroup.GetOutAllowList()
sq := manager.Query().NotEquals("id", secgroup.Id)
secgroups := []SSecurityGroup{}
err = db.FetchModelObjects(manager, sq, &secgroups)
if err != nil {
return nil, err
}
secgroupIds := []string{}
for i := 0; i < len(secgroups); i++ {
_inAllowList := secgroups[i].GetInAllowList()
if !inAllowList.Equals(_inAllowList) {
continue
}
_outAllowList := secgroups[i].GetOutAllowList()
if !outAllowList.Equals(_outAllowList) {
continue
}
secgroupIds = append(secgroupIds, secgroups[i].Id)
}
q = q.In("id", secgroupIds)
}
serverStr := input.Server
if len(serverStr) > 0 {
guest, _, err := ValidateGuestResourceInput(userCred, input.ServerResourceInput)
if err != nil {
return nil, errors.Wrap(err, "ValidateGuestResourceInput")
}
serverId := guest.GetId()
filters := []sqlchemy.ICondition{}
filters = append(filters, sqlchemy.In(q.Field("id"), GuestManager.Query("secgrp_id").Equals("id", serverId).SubQuery()))
filters = append(filters, sqlchemy.In(q.Field("id"), GuestsecgroupManager.Query("secgroup_id").Equals("guest_id", serverId).SubQuery()))
isAdmin := false
admin := (input.ServerFilterListInput.Admin != nil && *input.ServerFilterListInput.Admin)
if consts.IsRbacEnabled() {
allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList)
if allowScope == rbacutils.ScopeSystem || allowScope == rbacutils.ScopeDomain {
isAdmin = true
}
} else if userCred.HasSystemAdminPrivilege() && admin {
isAdmin = true
}
if isAdmin {
filters = append(filters, sqlchemy.In(q.Field("id"), GuestManager.Query("admin_secgrp_id").Equals("id", serverId).SubQuery()))
}
q = q.Filter(sqlchemy.OR(filters...))
}
if input.IsDirty != nil {
if *input.IsDirty {
q = q.IsTrue("is_dirty")
} else {
q = q.IsFalse("is_dirty")
}
}
return q, nil
}
func (manager *SSecurityGroupManager) OrderByExtraFields(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
input api.SecgroupListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.SharableVirtualResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.OrderByExtraFields")
}
orderByCache := input.OrderByCacheCnt
if sqlchemy.SQL_ORDER_ASC.Equals(orderByCache) || sqlchemy.SQL_ORDER_DESC.Equals(orderByCache) {
caches := SecurityGroupCacheManager.Query().SubQuery()
cacheQ := caches.Query(
caches.Field("secgroup_id"),
sqlchemy.COUNT("cache_cnt"),
)
cacheSQ := cacheQ.GroupBy(caches.Field("secgroup_id")).SubQuery()
q = q.LeftJoin(cacheSQ, sqlchemy.Equals(q.Field("id"), cacheSQ.Field("secgroup_id")))
if sqlchemy.SQL_ORDER_ASC.Equals(orderByCache) {
q = q.Asc(cacheSQ.Field("cache_cnt"))
} else {
q = q.Desc(cacheSQ.Field("cache_cnt"))
}
}
orderByGuest := input.OrderByGuestCnt
if sqlchemy.SQL_ORDER_ASC.Equals(orderByGuest) || sqlchemy.SQL_ORDER_DESC.Equals(orderByGuest) {
guests := GuestManager.Query().SubQuery()
guestsecgroups := GuestsecgroupManager.Query().SubQuery()
q1 := guests.Query(guests.Field("id").Label("guest_id"),
guests.Field("secgrp_id").Label("secgroup_id"))
q2 := guestsecgroups.Query(guestsecgroups.Field("guest_id"),
guestsecgroups.Field("secgroup_id"))
uq := sqlchemy.Union(q1, q2)
uQ := uq.Query(
uq.Field("secgroup_id"),
sqlchemy.COUNT("guest_cnt", uq.Field("guest_id")),
)
sq := uQ.GroupBy(uq.Field("secgroup_id")).SubQuery()
q = q.LeftJoin(sq, sqlchemy.Equals(q.Field("id"), sq.Field("secgroup_id")))
if sqlchemy.SQL_ORDER_ASC.Equals(orderByGuest) {
q = q.Asc(sq.Field("guest_cnt"))
} else {
q = q.Desc(sq.Field("guest_cnt"))
}
}
return q, nil
}
func (manager *SSecurityGroupManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
return q, httperrors.ErrNotFound
}
func (self *SSecurityGroup) GetGuestsQuery() *sqlchemy.SQuery {
guests := GuestManager.Query().SubQuery()
return guests.Query().Filter(
sqlchemy.OR(
sqlchemy.Equals(guests.Field("secgrp_id"), self.Id),
sqlchemy.Equals(guests.Field("admin_secgrp_id"), self.Id),
sqlchemy.In(guests.Field("id"), GuestsecgroupManager.Query("guest_id").Equals("secgroup_id", self.Id).SubQuery()),
),
).Filter(sqlchemy.NotIn(guests.Field("hypervisor"), []string{api.HYPERVISOR_CONTAINER, api.HYPERVISOR_BAREMETAL, api.HYPERVISOR_ESXI}))
}
func (self *SSecurityGroup) GetGuestsCount() (int, error) {
return self.GetGuestsQuery().CountWithError()
}
func (self *SSecurityGroup) GetGuests() []SGuest {
guests := []SGuest{}
q := self.GetGuestsQuery()
err := db.FetchModelObjects(GuestManager, q, &guests)
if err != nil {
log.Errorf("GetGuests fail %s", err)
return nil
}
return guests
}
func (self *SSecurityGroup) GetSecgroupCacheQuery() *sqlchemy.SQuery {
return SecurityGroupCacheManager.Query().Equals("secgroup_id", self.Id)
}
func (self *SSecurityGroup) GetSecgroupCacheCount() (int, error) {
return self.GetSecgroupCacheQuery().CountWithError()
}
func (self *SSecurityGroup) getDesc() jsonutils.JSONObject {
desc := jsonutils.NewDict()
desc.Add(jsonutils.NewString(self.Name), "name")
desc.Add(jsonutils.NewString(self.Id), "id")
//desc.Add(jsonutils.NewString(self.getSecurityRuleString("")), "security_rules")
return desc
}
func (manager *SSecurityGroupManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.SecgroupDetails {
rows := make([]api.SecgroupDetails, len(objs))
virtRows := manager.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
for i := range rows {
rows[i] = api.SecgroupDetails{
SharableVirtualResourceDetails: virtRows[i],
}
sg := objs[i].(*SSecurityGroup)
rows[i].GuestCnt = len(sg.GetGuests())
rows[i].CacheCnt, _ = sg.GetSecgroupCacheCount()
if !isList {
rows[i].Rules = sg.getSecurityRuleString("")
rows[i].InRules = sg.getSecurityRuleString("in")
rows[i].OutRules = sg.getSecurityRuleString("out")
}
}
return rows
}
func (self *SSecurityGroup) GetExtraDetails(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
isList bool,
) (api.SecgroupDetails, error) {
return api.SecgroupDetails{}, nil
}
func (manager *SSecurityGroupManager) ValidateCreateData(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
input api.SSecgroupCreateInput,
) (api.SSecgroupCreateInput, error) {
var err error
// TODO: check set pending quota
input.Status = api.SECGROUP_STATUS_READY
for i, rule := range input.Rules {
err = rule.Check()
if err != nil {
return input, httperrors.NewInputParameterError("rule %d is invalid: %s", i, err)
}
}
input.SharableVirtualResourceCreateInput, err = manager.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
if err != nil {
return input, err
}
return input, nil
}
func (self *SSecurityGroup) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
self.SSharableVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
input := &api.SSecgroupCreateInput{}
data.Unmarshal(input)
for _, r := range input.Rules {
rule := &SSecurityGroupRule{
Priority: int64(r.Priority),
Protocol: r.Protocol,
Ports: r.Ports,
Direction: r.Direction,
CIDR: r.CIDR,
Action: r.Action,
Description: r.Description,
}
rule.SecgroupId = self.Id
SecurityGroupRuleManager.TableSpec().Insert(rule)
}
}
func (manager *SSecurityGroupManager) FetchSecgroupById(secId string) *SSecurityGroup {
if len(secId) > 0 {
secgrp, _ := manager.FetchById(secId)
if secgrp != nil {
return secgrp.(*SSecurityGroup)
}
}
return nil
}
func (self *SSecurityGroup) getSecurityRules(direction string) (rules []SSecurityGroupRule) {
secgrouprules := SecurityGroupRuleManager.Query().SubQuery()
sql := secgrouprules.Query().Filter(sqlchemy.Equals(secgrouprules.Field("secgroup_id"), self.Id)).Desc("priority")
if len(direction) > 0 && utils.IsInStringArray(direction, []string{"in", "out"}) {
sql = sql.Equals("direction", direction)
}
if err := db.FetchModelObjects(SecurityGroupRuleManager, sql, &rules); err != nil {
log.Errorf("GetGuests fail %s", err)
return
}
return
}
func (self *SSecurityGroup) GetSecRules(direction string) []secrules.SecurityRule {
rules := make([]secrules.SecurityRule, 0)
for _, _rule := range self.getSecurityRules(direction) {
//这里没必要拆分为单个单个的端口,到公有云那边适配
rule, err := _rule.toRule()
if err != nil {
log.Errorln(err)
continue
}
rules = append(rules, *rule)
}
return rules
}
func (self *SSecurityGroup) getSecurityRuleString(direction string) string {
secgrouprules := self.getSecurityRules(direction)
var rules []string
for _, rule := range secgrouprules {
rules = append(rules, rule.String())
}
return strings.Join(rules, SECURITY_GROUP_SEPARATOR)
}
func totalSecurityGroupCount(scope rbacutils.TRbacScope, ownerId mcclient.IIdentityProvider) (int, error) {
q := SecurityGroupManager.Query()
switch scope {
case rbacutils.ScopeSystem:
// do nothing
case rbacutils.ScopeDomain:
q = q.Equals("domain_id", ownerId.GetProjectDomainId())
case rbacutils.ScopeProject:
q = q.Equals("tenant_id", ownerId.GetProjectId())
}
return q.CountWithError()
}
func (self *SSecurityGroup) AllowPerformUncacheSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "uncache-secgroup")
}
func (self *SSecurityGroup) PerformUncacheSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
cacheV := validators.NewModelIdOrNameValidator("secgroupcache", "secgroupcache", nil)
err := cacheV.Validate(data.(*jsonutils.JSONDict))
if err != nil {
return nil, err
}
cache := cacheV.Model.(*SSecurityGroupCache)
return nil, cache.StartSecurityGroupCacheDeleteTask(ctx, userCred, "")
}
func (self *SSecurityGroup) AllowPerformCacheSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "cache-secgroup")
}
func (self *SSecurityGroup) PerformCacheSecgroup(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
vpcV := validators.NewModelIdOrNameValidator("vpc", "vpc", nil)
err := vpcV.Validate(data.(*jsonutils.JSONDict))
if err != nil {
return nil, err
}
vpc := vpcV.Model.(*SVpc)
if len(vpc.ExternalId) == 0 {
return nil, httperrors.NewInputParameterError("vpc %s(%s) is not a managed resouce", vpc.Name, vpc.Id)
}
region, err := vpc.GetRegion()
if err != nil {
return nil, err
}
classic, _ := data.Bool("classic")
if classic && !region.GetDriver().IsSupportClassicSecurityGroup() {
return nil, httperrors.NewInputParameterError("Not support cache classic security group")
}
return nil, self.StartSecurityGroupCacheTask(ctx, userCred, vpc.Id, classic, "")
}
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")
params.Add(jsonutils.NewBool(classic), "classic")
task, err := taskman.TaskManager.NewTask(ctx, "SecurityGroupCacheTask", self, userCred, params, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SSecurityGroup) AllowPerformAddRule(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "add-rule")
}
func (self *SSecurityGroup) PerformAddRule(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
secgrouprule := &SSecurityGroupRule{}
secgrouprule.SecgroupId = self.Id
secgrouprule.SetModelManager(SecurityGroupRuleManager, secgrouprule)
if err := data.Unmarshal(secgrouprule); err != nil {
return nil, err
}
if len(secgrouprule.CIDR) > 0 {
if !regutils.MatchCIDR(secgrouprule.CIDR) && !regutils.MatchIPAddr(secgrouprule.CIDR) {
return nil, httperrors.NewInputParameterError("invalid ip address: %s", secgrouprule.CIDR)
}
} else {
secgrouprule.CIDR = "0.0.0.0/0"
}
rule := secrules.SecurityRule{
Priority: int(secgrouprule.Priority),
Direction: secrules.TSecurityRuleDirection(secgrouprule.Direction),
Action: secrules.TSecurityRuleAction(secgrouprule.Action),
Protocol: secgrouprule.Protocol,
Ports: []int{},
PortStart: -1,
PortEnd: -1,
}
if err := rule.ParsePorts(secgrouprule.Ports); err != nil {
return nil, httperrors.NewInputParameterError(err.Error())
}
if err := rule.ValidateRule(); err != nil {
return nil, httperrors.NewInputParameterError(err.Error())
}
if err := SecurityGroupRuleManager.TableSpec().Insert(secgrouprule); err != nil {
return nil, httperrors.NewInputParameterError(err.Error())
}
self.DoSync(ctx, userCred)
return nil, nil
}
func (self *SSecurityGroup) AllowPerformClone(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return true
}
func (self *SSecurityGroup) PerformClone(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
name, _ := data.GetString("name")
if len(name) == 0 {
return nil, httperrors.NewMissingParameterError("name")
}
_, err := SecurityGroupManager.FetchByName(userCred, name)
if err != nil {
if err != sql.ErrNoRows {
return nil, httperrors.NewInternalServerError("FetchByName fail %s", err)
}
} else {
return nil, httperrors.NewDuplicateNameError("name", name)
}
secgroup := &SSecurityGroup{}
secgroup.SetModelManager(SecurityGroupManager, secgroup)
secgroup.Name = name
secgroup.Description, _ = data.GetString("description")
secgroup.ProjectId = userCred.GetProjectId()
secgroup.DomainId = userCred.GetProjectDomainId()
err = SecurityGroupManager.TableSpec().Insert(secgroup)
if err != nil {
return nil, err
//db.OpsLog.LogCloneEvent(self, secgroup, userCred, nil)
}
secgrouprules := self.getSecurityRules("")
for _, rule := range secgrouprules {
secgrouprule := &SSecurityGroupRule{}
secgrouprule.SetModelManager(SecurityGroupRuleManager, secgrouprule)
secgrouprule.Priority = rule.Priority
secgrouprule.Protocol = rule.Protocol
secgrouprule.Ports = rule.Ports
secgrouprule.Direction = rule.Direction
secgrouprule.CIDR = rule.CIDR
secgrouprule.Action = rule.Action
secgrouprule.Description = rule.Description
secgrouprule.SecgroupId = secgroup.Id
if err := SecurityGroupRuleManager.TableSpec().Insert(secgrouprule); err != nil {
return nil, err
}
}
logclient.AddActionLogWithContext(ctx, secgroup, logclient.ACT_CREATE, secgroup.GetShortDesc(ctx), userCred, true)
return nil, nil
}
func (self *SSecurityGroup) AllowPerformMerge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, self, "merge")
}
func (self *SSecurityGroup) PerformMerge(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
secgroupIds := jsonutils.GetQueryStringArray(data, "secgroups")
if len(secgroupIds) == 0 {
return nil, httperrors.NewMissingParameterError("secgroups")
}
inAllowList := self.GetInAllowList()
outAllowList := self.GetOutAllowList()
secgroups := []*SSecurityGroup{}
for _, secgroupId := range secgroupIds {
_secgroup, err := SecurityGroupManager.FetchByIdOrName(userCred, secgroupId)
if err != nil {
return nil, httperrors.NewResourceNotFoundError("failed to find secgroup %s error: %v", secgroupId, err)
}
secgroup := _secgroup.(*SSecurityGroup)
secgroup.SetModelManager(SecurityGroupManager, secgroup)
_inAllowList := secgroup.GetInAllowList()
if !inAllowList.Equals(_inAllowList) {
return nil, httperrors.NewUnsupportOperationError("secgroup %s rules not equals %s rules", secgroup.Name, self.Name)
}
_outAllowList := secgroup.GetOutAllowList()
if !outAllowList.Equals(_outAllowList) {
return nil, httperrors.NewUnsupportOperationError("secgroup %s rules not equals %s rules", secgroup.Name, self.Name)
}
secgroups = append(secgroups, secgroup)
}
for i := 0; i < len(secgroups); i++ {
secgroup := secgroups[i]
if err := self.migrateSecurityGroupCache(secgroup); err != nil {
return nil, err
}
if err := self.migrateGuestSecurityGroup(secgroup); err != nil {
return nil, err
}
secgroup.RealDelete(ctx, userCred)
}
self.DoSync(ctx, userCred)
return nil, nil
}
func (self *SSecurityGroup) GetOutAllowList() secrules.SecurityRuleSet {
rules := self.GetSecRules("out")
ruleSet := secrules.SecurityRuleSet(rules)
rules = append(rules, *secrules.MustParseSecurityRule("out:allow any"))
return ruleSet.AllowList()
}
func (self *SSecurityGroup) GetInAllowList() secrules.SecurityRuleSet {
rules := self.GetSecRules("in")
rules = append(rules, *secrules.MustParseSecurityRule("in:deny any"))
ruleSet := secrules.SecurityRuleSet(rules)
return ruleSet.AllowList()
}
func (self *SSecurityGroup) getSecurityGroupRuleSet() secrules.SecurityGroupRuleSet {
rules := self.GetSecRules("")
srs := secrules.SecurityGroupRuleSet{}
for i := 0; i < len(rules); i++ {
srs.AddRule(rules[i])
}
return srs
}
func (self *SSecurityGroup) migrateSecurityGroupCache(secgroup *SSecurityGroup) error {
caches := []SSecurityGroupCache{}
q := SecurityGroupCacheManager.Query().Equals("secgroup_id", secgroup.Id)
err := db.FetchModelObjects(SecurityGroupCacheManager, q, &caches)
if err != nil {
return err
}
for i := 0; i < len(caches); i++ {
cache := caches[i]
_, err := db.Update(&cache, func() error {
cache.SecgroupId = self.Id
return nil
})
if err != nil {
return err
}
}
return nil
}
func (self *SSecurityGroup) migrateGuestSecurityGroup(secgroup *SSecurityGroup) error {
guests := secgroup.GetGuests()
for i := 0; i < len(guests); i++ {
guest := guests[i]
_, err := db.Update(&guest, func() error {
if guest.SecgrpId == secgroup.Id {
guest.SecgrpId = self.Id
}
if guest.AdminSecgrpId == secgroup.Id {
guest.AdminSecgrpId = self.Id
}
return nil
})
if err != nil {
return err
}
}
guestsecgroups, err := GuestsecgroupManager.GetGuestSecgroups(nil, secgroup)
if err != nil {
return err
}
for i := 0; i < len(guestsecgroups); i++ {
guestsecgroup := guestsecgroups[i]
_, err := db.Update(&guestsecgroup, func() error {
guestsecgroup.SecgroupId = self.Id
return nil
})
if err != nil {
return err
}
}
return nil
}
func (manager *SSecurityGroupManager) getSecurityGroups() ([]SSecurityGroup, error) {
secgroups := make([]SSecurityGroup, 0)
q := manager.Query()
if err := db.FetchModelObjects(manager, q, &secgroups); err != nil {
return nil, err
} else {
return secgroups, nil
}
}
func (manager *SSecurityGroupManager) newFromCloudSecgroup(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, extSec cloudprovider.ICloudSecurityGroup) (*SSecurityGroup, error) {
rules, err := extSec.GetRules()
if err != nil {
return nil, err
}
inRules := secrules.SecurityRuleSet{}
outRules := secrules.SecurityRuleSet{}
for i := 0; i < len(rules); i++ {
if rules[i].Direction == secrules.DIR_IN {
inRules = append(inRules, rules[i])
} else {
outRules = append(outRules, rules[i])
}
}
sort.Sort(inRules)
sort.Sort(outRules)
inAllowList := inRules.AllowList()
outAllowList := outRules.AllowList()
// 查询所有共享或与provider在同一项目的安全组比对寻找一个与云上安全组规则相同的安全组
secgroups := []SSecurityGroup{}
q := manager.Query()
q = q.Filter(
sqlchemy.OR(
sqlchemy.Equals(q.Field("tenant_id"), provider.ProjectId),
sqlchemy.AND(
sqlchemy.IsTrue(q.Field("is_public")),
sqlchemy.Equals(q.Field("public_scope"), rbacutils.ScopeSystem),
),
),
)
if err := db.FetchModelObjects(manager, q, &secgroups); err != nil {
log.Errorf("failed to fetch secgroups %v", err)
}
for _, secgroup := range secgroups {
_inAllowList := secgroup.GetInAllowList()
_outAllowList := secgroup.GetOutAllowList()
if outAllowList.Equals(_outAllowList) && inAllowList.Equals(_inAllowList) {
return &secgroup, nil
}
}
lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, userCred))
defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, userCred))
secgroup := SSecurityGroup{}
secgroup.SetModelManager(manager, &secgroup)
newName, err := db.GenerateName(manager, userCred, extSec.GetName())
if err != nil {
return nil, err
}
secgroup.Name = newName
secgroup.Description = extSec.GetDescription()
secgroup.ProjectId = provider.ProjectId
secgroup.DomainId = provider.DomainId
if err := manager.TableSpec().Insert(&secgroup); err != nil {
return nil, err
}
//这里必须先同步下规则,不然下次对比此安全组规则为空
SecurityGroupRuleManager.SyncRules(ctx, userCred, &secgroup, inRules)
SecurityGroupRuleManager.SyncRules(ctx, userCred, &secgroup, outRules)
db.OpsLog.LogEvent(&secgroup, db.ACT_CREATE, secgroup.GetShortDesc(ctx), userCred)
return &secgroup, nil
}
func (manager *SSecurityGroupManager) DelaySync(ctx context.Context, userCred mcclient.TokenCredential, idStr string) {
if secgrp := manager.FetchSecgroupById(idStr); secgrp == nil {
log.Errorf("DelaySync secgroup failed")
} else {
needSync := false
lockman.LockObject(ctx, secgrp)
defer lockman.ReleaseObject(ctx, secgrp)
if secgrp.IsDirty {
if _, err := db.Update(secgrp, func() error {
secgrp.IsDirty = false
return nil
}); err != nil {
log.Errorf("Update Security Group error: %s", err.Error())
}
needSync = true
}
if needSync {
for _, guest := range secgrp.GetGuests() {
guest.StartSyncTask(ctx, userCred, true, "")
}
}
}
}
func (self *SSecurityGroup) DoSync(ctx context.Context, userCred mcclient.TokenCredential) {
if _, err := db.Update(self, func() error {
self.IsDirty = true
return nil
}); err != nil {
log.Errorf("Update Security Group error: %s", err.Error())
}
time.AfterFunc(10*time.Second, func() {
SecurityGroupManager.DelaySync(ctx, userCred, self.Id)
})
}
func (manager *SSecurityGroupManager) InitializeData() error {
_, err := manager.FetchById("default")
if err != nil && err != sql.ErrNoRows {
log.Errorf("find default secgroup fail %s", err)
return err
}
if err == sql.ErrNoRows {
var secGrp *SSecurityGroup
secGrp = &SSecurityGroup{}
secGrp.SetModelManager(manager, secGrp)
secGrp.Id = "default"
secGrp.Name = "Default"
secGrp.Status = api.SECGROUP_STATUS_READY
secGrp.ProjectId = auth.AdminCredential().GetProjectId()
secGrp.DomainId = auth.AdminCredential().GetProjectDomainId()
// secGrp.IsEmulated = false
secGrp.IsPublic = true
err = manager.TableSpec().Insert(secGrp)
if err != nil {
log.Errorf("Insert default secgroup failed!!! %s", err)
return err
}
defRule := SSecurityGroupRule{}
defRule.SetModelManager(SecurityGroupRuleManager, &defRule)
defRule.Direction = secrules.DIR_IN
defRule.Protocol = secrules.PROTO_ANY
defRule.Priority = 1
defRule.CIDR = "0.0.0.0/0"
defRule.Action = string(secrules.SecurityRuleAllow)
defRule.SecgroupId = "default"
err = SecurityGroupRuleManager.TableSpec().Insert(&defRule)
if err != nil {
log.Errorf("Insert default secgroup rule fail %s", err)
return err
}
}
guests := make([]SGuest, 0)
q := GuestManager.Query()
q = q.Filter(sqlchemy.OR(sqlchemy.IsEmpty(q.Field("secgrp_id")), sqlchemy.IsNull(q.Field("secgrp_id"))))
err = db.FetchModelObjects(GuestManager, q, &guests)
if err != nil {
log.Errorf("fetch guests without secgroup fail %s", err)
return err
}
for i := 0; i < len(guests); i += 1 {
db.Update(&guests[i], func() error {
guests[i].SecgrpId = "default"
return nil
})
}
secgroups := []SSecurityGroup{}
q = SecurityGroupManager.Query().NotEquals("status", api.SECGROUP_STATUS_READY)
err = db.FetchModelObjects(manager, q, &secgroups)
if err != nil {
return errors.Wrap(err, "db.FetchModelObjects")
}
for i := range secgroups {
db.Update(&secgroups[i], func() error {
secgroups[i].Status = api.SECGROUP_STATUS_READY
return nil
})
}
return nil
}
func (self *SSecurityGroup) ValidateDeleteCondition(ctx context.Context) error {
cnt, err := self.GetGuestsCount()
if err != nil {
return httperrors.NewInternalServerError("GetGuestsCount fail %s", err)
}
if cnt > 0 {
return httperrors.NewNotEmptyError("the security group is in use")
}
if self.Id == "default" {
return httperrors.NewProtectedResourceError("not allow to delete default security group")
}
return self.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx)
}
func (self *SSecurityGroup) GetSecurityGroupCaches() []SSecurityGroupCache {
caches := []SSecurityGroupCache{}
q := SecurityGroupCacheManager.Query()
q = q.Filter(sqlchemy.Equals(q.Field("secgroup_id"), self.Id))
if err := db.FetchModelObjects(SecurityGroupCacheManager, q, &caches); err != nil {
log.Errorf("get secgroupcache for secgroup %s error: %v", self.Name, err)
}
return caches
}
func (self *SSecurityGroup) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
return self.StartDeleteSecurityGroupTask(ctx, userCred, jsonutils.NewDict(), "")
}
func (self *SSecurityGroup) StartDeleteSecurityGroupTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
self.SetStatus(userCred, api.SECGROUP_STATUS_DELETING, "")
task, err := taskman.TaskManager.NewTask(ctx, "SecurityGroupDeleteTask", self, userCred, params, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SSecurityGroup) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
log.Infof("do nothing for delete secgroup")
return nil
}
func (self *SSecurityGroup) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
rules := []SSecurityGroupRule{}
q := SecurityGroupRuleManager.Query().Equals("secgroup_id", self.Id)
err := db.FetchModelObjects(SecurityGroupRuleManager, q, &rules)
if err != nil {
return errors.Wrap(err, "db.FetchModelObjects")
}
for i := 0; i < len(rules); i++ {
lockman.LockObject(ctx, &rules[i])
defer lockman.ReleaseObject(ctx, &rules[i])
err := rules[i].Delete(ctx, userCred)
if err != nil {
return errors.Wrap(err, "rules[i].Delete")
}
}
return self.SVirtualResourceBase.Delete(ctx, userCred)
}
func (sg *SSecurityGroup) GetQuotaKeys() quotas.IQuotaKeys {
return quotas.OwnerIdProjectQuotaKeys(rbacutils.ScopeProject, sg.GetOwnerId())
}
func (sg *SSecurityGroup) GetUsages() []db.IUsage {
if sg.PendingDeleted || sg.Deleted {
return nil
}
usage := SProjectQuota{Secgroup: 1}
keys := sg.GetQuotaKeys()
usage.SetKeys(keys)
return []db.IUsage{
&usage,
}
}