Files
cloudpods/pkg/compute/models/secgroupcache.go

268 lines
9.4 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"
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/pkg/util/stringutils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)
type SSecurityGroupCacheManager struct {
db.SResourceBaseManager
}
type SSecurityGroupCache struct {
db.SResourceBase
SManagedResourceBase
Id string `width:"128" charset:"ascii" primary:"true" list:"user"`
SecgroupId string `width:"128" charset:"ascii" create:"required"`
VpcId string `width:"128" charset:"ascii" create:"required"`
CloudregionId string `width:"128" charset:"ascii" create:"required"`
ExternalId string `width:"256" charset:"utf8" index:"true" list:"admin" create:"admin_optional"`
}
var SecurityGroupCacheManager *SSecurityGroupCacheManager
func init() {
SecurityGroupCacheManager = &SSecurityGroupCacheManager{
SResourceBaseManager: db.NewResourceBaseManager(
SSecurityGroupCache{},
"secgroupcache_tbl",
"secgroupcache",
"secgroupcaches",
),
}
SecurityGroupCacheManager.SetVirtualObject(SecurityGroupCacheManager)
}
func (self *SSecurityGroupCache) BeforeInsert() {
if len(self.Id) == 0 {
self.Id = stringutils.UUID4()
}
}
func (manager *SSecurityGroupCacheManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return false
}
func (manager *SSecurityGroupCacheManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return true
}
func (self *SSecurityGroupCache) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
return false
}
func (self *SSecurityGroupCache) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return false
}
func (manager *SSecurityGroupCacheManager) FilterById(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery {
return q.Equals("id", idStr)
}
func (manager *SSecurityGroupCacheManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
q, err := manager.SResourceBaseManager.ListItemFilter(ctx, q, userCred, query)
if err != nil {
return nil, err
}
if defsecgroup, _ := query.GetString("secgroup"); len(defsecgroup) > 0 {
secgroup, err := SecurityGroupManager.FetchByIdOrName(userCred, defsecgroup)
if err != nil {
if err == sql.ErrNoRows {
return nil, httperrors.NewResourceNotFoundError2(SecurityGroupManager.Keyword(), defsecgroup)
} else {
return nil, httperrors.NewGeneralError(err)
}
}
q = q.Equals("secgroup_id", secgroup.GetId())
}
return q, nil
}
func (self *SSecurityGroupCache) GetIRegion() (cloudprovider.ICloudRegion, error) {
provider, err := self.GetDriver()
if err != nil {
return nil, err
}
if region := CloudregionManager.FetchRegionById(self.CloudregionId); region != nil {
return provider.GetIRegionById(region.ExternalId)
}
return nil, fmt.Errorf("failed to find iregion for secgroupcache %s vpc: %s externalId: %s", self.Id, self.VpcId, self.ExternalId)
}
func (self *SSecurityGroupCache) DeleteCloudSecurityGroup(ctx context.Context, userCred mcclient.TokenCredential) error {
if len(self.ExternalId) > 0 {
iregion, err := self.GetIRegion()
if err != nil {
return err
}
return iregion.DeleteSecurityGroup(self.VpcId, self.ExternalId)
}
return nil
}
func (self *SSecurityGroupCache) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
if err := self.DeleteCloudSecurityGroup(ctx, userCred); err != nil {
log.Errorf("delete secgroup cache %v error: %v", self, err)
}
return db.DeleteModel(ctx, userCred, self)
}
func (manager *SSecurityGroupCacheManager) GetSecgroupCache(ctx context.Context, userCred mcclient.TokenCredential, secgroupId, vpcId string, regionId string, providerId string) (*SSecurityGroupCache, error) {
secgroupCache := SSecurityGroupCache{}
query := manager.Query()
cond := sqlchemy.AND(sqlchemy.Equals(query.Field("secgroup_id"), secgroupId), sqlchemy.Equals(query.Field("vpc_id"), vpcId), sqlchemy.Equals(query.Field("cloudregion_id"), regionId), sqlchemy.Equals(query.Field("manager_id"), providerId))
query = query.Filter(cond)
count, err := query.CountWithError()
if err != nil {
return nil, err
}
if count == 0 {
return nil, nil
}
query.First(&secgroupCache)
secgroupCache.SetModelManager(manager, &secgroupCache)
return &secgroupCache, nil
}
func (manager *SSecurityGroupCacheManager) NewCache(ctx context.Context, userCred mcclient.TokenCredential, secgroupId, vpcId, regionId string, providerId string) (*SSecurityGroupCache, error) {
lockman.LockClass(ctx, manager, userCred.GetProjectId())
defer lockman.ReleaseClass(ctx, manager, userCred.GetProjectId())
secgroupCache := &SSecurityGroupCache{
SecgroupId: secgroupId,
VpcId: vpcId,
CloudregionId: regionId,
}
secgroupCache.ManagerId = providerId
secgroupCache.SetModelManager(manager, secgroupCache)
if err := manager.TableSpec().Insert(secgroupCache); err != nil {
log.Errorf("insert secgroupcache error: %v", err)
return nil, err
}
return secgroupCache, nil
}
func (manager *SSecurityGroupCacheManager) Register(ctx context.Context, userCred mcclient.TokenCredential, secgroupId, vpcId, regionId string, providerId string) (*SSecurityGroupCache, error) {
secgroupCache, err := manager.GetSecgroupCache(ctx, userCred, secgroupId, vpcId, regionId, providerId)
if err != nil {
return nil, err
}
if secgroupCache != nil {
return secgroupCache, nil
}
return manager.NewCache(ctx, userCred, secgroupId, vpcId, regionId, providerId)
}
func (manager *SSecurityGroupCacheManager) getSecgroupcachesByProvider(provider *SCloudprovider) ([]SSecurityGroupCache, error) {
q := manager.Query().Equals("manager_id", provider.Id)
caches := []SSecurityGroupCache{}
if err := db.FetchModelObjects(manager, q, &caches); err != nil {
return nil, err
}
return caches, nil
}
func (self *SSecurityGroupCache) GetSecgroup() (*SSecurityGroup, error) {
model, err := SecurityGroupManager.FetchById(self.SecgroupId)
if err != nil {
return nil, fmt.Errorf("failed to fetch secgroup by %s", self.SecgroupId)
}
return model.(*SSecurityGroup), nil
}
func (manager *SSecurityGroupCacheManager) SyncSecurityGroupCaches(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, secgroups []cloudprovider.ICloudSecurityGroup, vpc *SVpc) ([]SSecurityGroup, []cloudprovider.ICloudSecurityGroup, compare.SyncResult) {
lockman.LockClass(ctx, manager, db.GetLockClassKey(manager, userCred))
defer lockman.ReleaseClass(ctx, manager, db.GetLockClassKey(manager, userCred))
localSecgroups := []SSecurityGroup{}
remoteSecgroups := []cloudprovider.ICloudSecurityGroup{}
syncResult := compare.SyncResult{}
dbSecgroupcaches, err := manager.getSecgroupcachesByProvider(provider)
if err != nil {
syncResult.Error(err)
return nil, nil, syncResult
}
removed := []SSecurityGroupCache{}
commondb := []SSecurityGroupCache{}
commonext := []cloudprovider.ICloudSecurityGroup{}
added := []cloudprovider.ICloudSecurityGroup{}
if err := compare.CompareSets(dbSecgroupcaches, secgroups, &removed, &commondb, &commonext, &added); err != nil {
syncResult.Error(err)
return nil, nil, syncResult
}
//removed暂时删不了, 因为不能确定是哪个vpc底下的,有可能误删
//相同的不能同步, 原因: 多个平台的安全组可能共用一个本地安全组,下面仅仅是新加的安全组
for i := 0; i < len(added); i++ {
secgroup, err := SecurityGroupManager.newFromCloudSecgroup(ctx, userCred, provider, added[i])
if err != nil {
syncResult.AddError(err)
continue
}
cache, err := manager.NewCache(ctx, userCred, secgroup.Id, added[i].GetVpcId(), vpc.CloudregionId, provider.Id)
if err != nil {
syncResult.AddError(fmt.Errorf("failed to create secgroup cache for secgroup %s(%s) provider: %s: %s", secgroup.Name, secgroup.Name, provider.Name, err))
continue
}
if err = cache.SetExternalId(userCred, added[i].GetGlobalId()); err != nil {
syncResult.AddError(err)
continue
}
localSecgroups = append(localSecgroups, *secgroup)
remoteSecgroups = append(remoteSecgroups, added[i])
syncResult.Add()
}
return localSecgroups, remoteSecgroups, syncResult
}
func (self *SSecurityGroupCache) SetExternalId(userCred mcclient.TokenCredential, externalId string) error {
diff, err := db.Update(self, func() error {
self.ExternalId = externalId
return nil
})
if err != nil {
return err
}
db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
return nil
}
func (self SSecurityGroupCache) GetExternalId() string {
return self.ExternalId
}