diff --git a/cmd/climc/shell/compute/secgroupcaches.go b/cmd/climc/shell/compute/secgroupcaches.go index 6f46b9140f..4861470b8f 100644 --- a/cmd/climc/shell/compute/secgroupcaches.go +++ b/cmd/climc/shell/compute/secgroupcaches.go @@ -15,48 +15,16 @@ package compute import ( - "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/cmd/climc/shell" "yunion.io/x/onecloud/pkg/mcclient/modules" - "yunion.io/x/onecloud/pkg/mcclient/options" + "yunion.io/x/onecloud/pkg/mcclient/options/compute" ) func init() { - type SecGroupCacheListOptions struct { - options.BaseListOptions - Secgroup string `help:"Secgroup ID or Name"` - } - - R(&SecGroupCacheListOptions{}, "secgroup-cache-list", "List security group caches", func(s *mcclient.ClientSession, args *SecGroupCacheListOptions) error { - params, err := options.ListStructToParams(args) - if err != nil { - return err - } - result, err := modules.SecGroupCaches.List(s, params) - if err != nil { - return err - } - printList(result, modules.SecGroupCaches.GetColumns(s)) - return nil - }) - type SecGroupCacheIdOptions struct { - ID string `help:"ID or Name or secgroup cache"` - } - R(&SecGroupCacheIdOptions{}, "secgroup-cache-show", "Show security group cache", func(s *mcclient.ClientSession, args *SecGroupCacheIdOptions) error { - result, err := modules.SecGroupCaches.Get(s, args.ID, nil) - if err != nil { - return err - } - printObject(result) - return nil - }) - - R(&SecGroupCacheIdOptions{}, "secgroup-cache-delete", "Delete security group cache", func(s *mcclient.ClientSession, args *SecGroupCacheIdOptions) error { - result, err := modules.SecGroupCaches.Delete(s, args.ID, nil) - if err != nil { - return err - } - printObject(result) - return nil - }) - + cmd := shell.NewResourceCmd(&modules.SecGroupCaches).WithKeyword("secgroup-cache") + cmd.List(&compute.SecGroupCacheListOptions{}) + cmd.Show(&compute.SecGroupCacheIdOptions{}) + cmd.Delete(&compute.SecGroupCacheIdOptions{}) + cmd.Perform("syncstatus", &compute.SecGroupCacheIdOptions{}) + cmd.Get("references", &compute.SecGroupCacheIdOptions{}) } diff --git a/pkg/apis/compute/secgroupcache_const.go b/pkg/apis/compute/secgroupcache_const.go index f151922330..7410b32e75 100644 --- a/pkg/apis/compute/secgroupcache_const.go +++ b/pkg/apis/compute/secgroupcache_const.go @@ -16,6 +16,7 @@ package compute const ( SECGROUP_CACHE_STATUS_READY = "ready" + SECGROUP_CACHE_STATUS_UNKNOWN = "unknown" SECGROUP_CACHE_STATUS_DELETING = "deleting" SECGROUP_CACHE_STATUS_CACHING = "caching" SECGROUP_CACHE_STATUS_DELETE_FAILED = "delete_failed" diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 41c79143cf..41cd68b4d5 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -415,6 +415,7 @@ type ICloudSecurityGroup interface { GetVpcId() string SyncRules(common, inAdds, outAdds, inDels, outDels []SecurityRule) error + GetReferences() ([]SecurityGroupReference, error) Delete() error } diff --git a/pkg/cloudprovider/securitygroup.go b/pkg/cloudprovider/securitygroup.go index a3677fd526..beffb7274d 100644 --- a/pkg/cloudprovider/securitygroup.go +++ b/pkg/cloudprovider/securitygroup.go @@ -26,6 +26,10 @@ import ( "yunion.io/x/pkg/utils" ) +type SecurityGroupReference struct { + Id string +} + type SecDriver interface { GetDefaultSecurityGroupInRule() SecurityRule GetDefaultSecurityGroupOutRule() SecurityRule diff --git a/pkg/compute/models/purge.go b/pkg/compute/models/purge.go index 157624e93e..01d86428a7 100644 --- a/pkg/compute/models/purge.go +++ b/pkg/compute/models/purge.go @@ -1693,11 +1693,6 @@ func (cache *SSecurityGroupCache) purge(ctx context.Context, userCred mcclient.T lockman.LockObject(ctx, cache) defer lockman.ReleaseObject(ctx, cache) - err := cache.ValidateDeleteCondition(ctx) - if err != nil { - return err - } - return cache.RealDelete(ctx, userCred) } diff --git a/pkg/compute/models/secgroupcache.go b/pkg/compute/models/secgroupcache.go index 0178084cc6..e326d56439 100644 --- a/pkg/compute/models/secgroupcache.go +++ b/pkg/compute/models/secgroupcache.go @@ -55,6 +55,9 @@ type SSecurityGroupCache struct { SManagedResourceBase SSecurityGroupResourceBase + // 被其他安全组引用的次数 + ReferenceCount int `nullable:"false" list:"user" json:"reference_count"` + // 安全组Id // SecgroupId string `width:"128" charset:"ascii" list:"user" create:"required"` @@ -388,14 +391,26 @@ func (self *SSecurityGroupCache) GetSecgroup() (*SSecurityGroup, error) { return model.(*SSecurityGroup), nil } -func (self *SSecurityGroupCache) syncWithCloudSecurityGroup(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, ext cloudprovider.ICloudSecurityGroup) ([]SSecurityGroupRule, error) { +func (self *SSecurityGroupCache) SyncBaseInfo(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudSecurityGroup) error { _, err := db.Update(self, func() error { self.Status = api.SECGROUP_CACHE_STATUS_READY self.Name = ext.GetName() self.Description = ext.GetDescription() self.ExternalProjectId = ext.GetProjectId() + references, err := ext.GetReferences() + if err == nil { + self.ReferenceCount = len(references) + } return nil }) + if err != nil { + return errors.Wrapf(err, "db.Update") + } + return nil +} + +func (self *SSecurityGroupCache) syncWithCloudSecurityGroup(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, ext cloudprovider.ICloudSecurityGroup) ([]SSecurityGroupRule, error) { + err := self.SyncBaseInfo(ctx, userCred, ext) if err != nil { return nil, errors.Wrapf(err, "db.Update") } @@ -519,6 +534,8 @@ func (manager *SSecurityGroupCacheManager) SyncSecurityGroupCaches(ctx context.C cache.Name = added[i].GetName() cache.Description = added[i].GetDescription() cache.ExternalId = added[i].GetGlobalId() + references, _ := added[i].GetReferences() + cache.ReferenceCount = len(references) return nil }) if err != nil { @@ -543,6 +560,40 @@ func (manager *SSecurityGroupCacheManager) SyncSecurityGroupCaches(ctx context.C return localSecgroups, remoteSecgroups, syncResult } +func (self *SSecurityGroupCache) AllowPerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsProjectAllowPerform(userCred, self, "syncstatus") +} + +// 同步安全组缓存状态 +func (self *SSecurityGroupCache) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DiskSyncstatusInput) (jsonutils.JSONObject, error) { + return nil, self.StartSyncstatusTask(ctx, userCred, "") +} + +func (self *SSecurityGroupCache) AllowGetDetailsReferences(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return db.IsAdminAllowGetSpec(userCred, self, "references") +} + +// 获取引用信息 +func (self *SSecurityGroupCache) GetDetailsReferences(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) ([]cloudprovider.SecurityGroupReference, error) { + iSecgroup, err := self.GetISecurityGroup() + if err != nil { + return nil, errors.Wrapf(err, "GetISecurityGroup") + } + return iSecgroup.GetReferences() +} + +func (self *SSecurityGroupCache) StartSyncstatusTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { + return StartResourceSyncStatusTask(ctx, userCred, self, "SecurityGroupCacheSyncstatusTask", "") +} + +func (self *SSecurityGroupCache) ValidateDeleteCondition(ctx context.Context) error { + if self.ReferenceCount > 0 && self.Status == api.SECGROUP_CACHE_STATUS_READY { + return httperrors.NewNotEmptyError("security group has been reference in %d security group", self.ReferenceCount) + } + + return self.SStatusStandaloneResourceBase.ValidateDeleteCondition(ctx) +} + func (self *SSecurityGroupCache) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { log.Infof("do nothing for delete secgroup cache") return nil diff --git a/pkg/compute/tasks/security_group_cache_syncstatus_task.go b/pkg/compute/tasks/security_group_cache_syncstatus_task.go new file mode 100644 index 0000000000..3d2c32c876 --- /dev/null +++ b/pkg/compute/tasks/security_group_cache_syncstatus_task.go @@ -0,0 +1,54 @@ +// 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 tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type SecurityGroupCacheSyncstatusTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(SecurityGroupCacheSyncstatusTask{}) +} + +func (self *SecurityGroupCacheSyncstatusTask) taskFailed(ctx context.Context, cache *models.SSecurityGroupCache, err error) { + cache.SetStatus(self.UserCred, api.SECGROUP_CACHE_STATUS_UNKNOWN, err.Error()) + self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) +} + +func (self *SecurityGroupCacheSyncstatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + cache := obj.(*models.SSecurityGroupCache) + + iSecgroup, err := cache.GetISecurityGroup() + if err != nil { + self.taskFailed(ctx, cache, errors.Wrapf(err, "GetISecurityGroup")) + return + } + + cache.SyncBaseInfo(ctx, self.GetUserCred(), iSecgroup) + + self.SetStageComplete(ctx, nil) +} diff --git a/pkg/mcclient/options/compute/secgroupcache.go b/pkg/mcclient/options/compute/secgroupcache.go new file mode 100644 index 0000000000..54cfacbfdf --- /dev/null +++ b/pkg/mcclient/options/compute/secgroupcache.go @@ -0,0 +1,42 @@ +// 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/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +type SecGroupCacheListOptions struct { + options.BaseListOptions + Secgroup string `help:"Secgroup ID or Name"` +} + +func (opts *SecGroupCacheListOptions) Params() (jsonutils.JSONObject, error) { + return options.ListStructToParams(opts) +} + +type SecGroupCacheIdOptions struct { + ID string `help:"ID or Name or secgroup cache"` +} + +func (opts *SecGroupCacheIdOptions) GetId() string { + return opts.ID +} + +func (opts *SecGroupCacheIdOptions) Params() (jsonutils.JSONObject, error) { + return nil, nil +} diff --git a/pkg/multicloud/aliyun/securitygroup.go b/pkg/multicloud/aliyun/securitygroup.go index 1fdfd4cf17..7eab5a1eeb 100644 --- a/pkg/multicloud/aliyun/securitygroup.go +++ b/pkg/multicloud/aliyun/securitygroup.go @@ -150,6 +150,52 @@ func (self *SSecurityGroup) Refresh() error { return jsonutils.Update(self, group) } +func (self *SSecurityGroup) GetReferences() ([]cloudprovider.SecurityGroupReference, error) { + references, err := self.vpc.region.DescribeSecurityGroupReferences(self.SecurityGroupId) + if err != nil { + return nil, errors.Wrapf(err, "DescribeSecurityGroupReferences") + } + ret := []cloudprovider.SecurityGroupReference{} + for _, reference := range references { + if reference.SecurityGroupId == self.SecurityGroupId { + for _, sec := range reference.ReferencingSecurityGroups.ReferencingSecurityGroup { + ret = append(ret, cloudprovider.SecurityGroupReference{ + Id: sec.SecurityGroupId, + }) + } + } + } + return ret, nil +} + +type ReferencingSecurityGroup struct { + AliUid string + SecurityGroupId string +} + +type ReferencingSecurityGroups struct { + ReferencingSecurityGroup []ReferencingSecurityGroup +} + +type SecurityGroupReferences struct { + SecurityGroupId string + ReferencingSecurityGroups ReferencingSecurityGroups +} + +func (self *SRegion) DescribeSecurityGroupReferences(id string) ([]SecurityGroupReferences, error) { + params := map[string]string{ + "RegionId": self.RegionId, + "SecurityGroupId.1": id, + } + resp, err := self.ecsRequest("DescribeSecurityGroupReferences", params) + if err != nil { + return nil, errors.Wrapf(err, "DescribeSecurityGroupReferences") + } + ret := []SecurityGroupReferences{} + err = resp.Unmarshal(&ret, "SecurityGroupReferences", "SecurityGroupReference") + return ret, errors.Wrapf(err, "resp.Unmarshal") +} + func (self *SRegion) GetSecurityGroups(vpcId, name string, securityGroupIds []string, offset int, limit int) ([]SSecurityGroup, int, error) { if limit > 50 || limit <= 0 { limit = 50 diff --git a/pkg/multicloud/aliyun/shell/secgroup.go b/pkg/multicloud/aliyun/shell/secgroup.go index a1adca20c5..0b913eb714 100644 --- a/pkg/multicloud/aliyun/shell/secgroup.go +++ b/pkg/multicloud/aliyun/shell/secgroup.go @@ -38,10 +38,10 @@ func init() { return nil }) - type SecurityGroupShowOptions struct { + type SecurityGroupIdOptions struct { ID string `help:"ID or name of security group"` } - shellutils.R(&SecurityGroupShowOptions{}, "security-group-show", "Show details of a security group", func(cli *aliyun.SRegion, args *SecurityGroupShowOptions) error { + shellutils.R(&SecurityGroupIdOptions{}, "security-group-show", "Show details of a security group", func(cli *aliyun.SRegion, args *SecurityGroupIdOptions) error { secgrp, err := cli.GetSecurityGroupDetails(args.ID) if err != nil { return err @@ -50,6 +50,15 @@ func init() { return nil }) + shellutils.R(&SecurityGroupIdOptions{}, "security-group-references", "Show references of a security group", func(cli *aliyun.SRegion, args *SecurityGroupIdOptions) error { + references, err := cli.DescribeSecurityGroupReferences(args.ID) + if err != nil { + return err + } + printList(references, 0, 0, 0, nil) + return nil + }) + type SecurityGroupCreateOptions struct { NAME string `help:"SecurityGroup name"` VpcId string `help:"VPC ID"` diff --git a/pkg/multicloud/qcloud/securitygroup.go b/pkg/multicloud/qcloud/securitygroup.go index c73d138eee..57a825d3de 100644 --- a/pkg/multicloud/qcloud/securitygroup.go +++ b/pkg/multicloud/qcloud/securitygroup.go @@ -136,6 +136,46 @@ func (self *SecurityGroupPolicy) String() string { return strings.Join(result, ";") } +type ReferredSecurityGroup struct { + SecurityGroupId string + ReferredSecurityGroupIds []string +} + +func (self *SSecurityGroup) GetReferences() ([]cloudprovider.SecurityGroupReference, error) { + references, err := self.region.DescribeSecurityGroupReferences(self.SecurityGroupId) + if err != nil { + return nil, errors.Wrapf(err, "DescribeSecurityGroupReferences") + } + ret := []cloudprovider.SecurityGroupReference{} + for _, refer := range references { + if refer.SecurityGroupId == self.SecurityGroupId { + for _, id := range refer.ReferredSecurityGroupIds { + ret = append(ret, cloudprovider.SecurityGroupReference{ + Id: id, + }) + } + } + } + return ret, nil +} + +func (self *SRegion) DescribeSecurityGroupReferences(id string) ([]ReferredSecurityGroup, error) { + params := map[string]string{ + "Region": self.Region, + "SecurityGroupIds.0": id, + } + resp, err := self.vpcRequest("DescribeSecurityGroupReferences", params) + if err != nil { + return nil, errors.Wrapf(err, "DescribeSecurityGroupReferences") + } + ret := []ReferredSecurityGroup{} + err = resp.Unmarshal(&ret, "ReferredSecurityGroupSet") + if err != nil { + return nil, errors.Wrapf(err, "resp.Unmarshal") + } + return ret, nil +} + func (self *SecurityGroupPolicy) toRules() []cloudprovider.SecurityRule { result := []cloudprovider.SecurityRule{} rule := cloudprovider.SecurityRule{ diff --git a/pkg/multicloud/qcloud/shell/securitygroup.go b/pkg/multicloud/qcloud/shell/securitygroup.go index 2644e95cac..d263a70474 100644 --- a/pkg/multicloud/qcloud/shell/securitygroup.go +++ b/pkg/multicloud/qcloud/shell/securitygroup.go @@ -55,6 +55,15 @@ func init() { return cloudprovider.ErrNotFound }) + shellutils.R(&SecurityGroupOptions{}, "security-group-references", "Show references of a security group", func(cli *qcloud.SRegion, args *SecurityGroupOptions) error { + references, err := cli.DescribeSecurityGroupReferences(args.ID) + if err != nil { + return err + } + printList(references, 0, 0, 0, nil) + return nil + }) + shellutils.R(&SecurityGroupOptions{}, "security-group-delete", "Delete SecurityGroup", func(cli *qcloud.SRegion, args *SecurityGroupOptions) error { return cli.DeleteSecurityGroup(args.ID) }) diff --git a/pkg/multicloud/secgroup_base.go b/pkg/multicloud/secgroup_base.go index 3735bbf3dd..428cdc0802 100644 --- a/pkg/multicloud/secgroup_base.go +++ b/pkg/multicloud/secgroup_base.go @@ -14,6 +14,12 @@ package multicloud +import "yunion.io/x/onecloud/pkg/cloudprovider" + type SSecurityGroup struct { SVirtualResourceBase } + +func (self *SSecurityGroup) GetReferences() ([]cloudprovider.SecurityGroupReference, error) { + return []cloudprovider.SecurityGroupReference{}, nil +}