mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-06-23 07:05:31 +08:00
951 lines
30 KiB
Go
951 lines
30 KiB
Go
// 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,
|
||
}
|
||
}
|