From 3ce18ea71fa8d67d717b33218ff23c9b4f7ac0c7 Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Mon, 16 Aug 2021 17:13:17 +0800 Subject: [PATCH] feat(region): add qcloud cdn --- cmd/climc/shell/compute/cdn_domains.go | 26 ++ pkg/apis/compute/cdn.go | 29 +- pkg/cloudcommon/db/external.go | 5 +- pkg/cloudprovider/cloudprovider.go | 13 + pkg/cloudprovider/consts.go | 3 +- pkg/cloudprovider/resources.go | 9 + pkg/compute/models/cdn_domains.go | 391 ++++++++++++++++++ pkg/compute/service/handlers.go | 2 + pkg/compute/tasks/cdn_domain_delete_task.go | 72 ++++ .../tasks/cdn_domain_syncstatus_task.go | 61 +++ .../tasks/cloud_provider_sync_info_task.go | 5 + pkg/mcclient/modules/mod_cdn_domains.go | 36 ++ pkg/mcclient/options/compute/cdn_domains.go | 29 ++ pkg/multicloud/qcloud/cdn.go | 86 +++- pkg/multicloud/qcloud/provider/provider.go | 8 + pkg/multicloud/qcloud/qcloud.go | 3 +- 16 files changed, 764 insertions(+), 14 deletions(-) create mode 100644 cmd/climc/shell/compute/cdn_domains.go create mode 100644 pkg/compute/models/cdn_domains.go create mode 100644 pkg/compute/tasks/cdn_domain_delete_task.go create mode 100644 pkg/compute/tasks/cdn_domain_syncstatus_task.go create mode 100644 pkg/mcclient/modules/mod_cdn_domains.go create mode 100644 pkg/mcclient/options/compute/cdn_domains.go diff --git a/cmd/climc/shell/compute/cdn_domains.go b/cmd/climc/shell/compute/cdn_domains.go new file mode 100644 index 0000000000..daab279a1d --- /dev/null +++ b/cmd/climc/shell/compute/cdn_domains.go @@ -0,0 +1,26 @@ +// 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/cmd/climc/shell" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options/compute" +) + +func init() { + cmd := shell.NewResourceCmd(&modules.CDNDomains) + cmd.List(&compute.CDNDomainListOptions{}) +} diff --git a/pkg/apis/compute/cdn.go b/pkg/apis/compute/cdn.go index 2a74b5ad7f..3ad0d5754c 100644 --- a/pkg/apis/compute/cdn.go +++ b/pkg/apis/compute/cdn.go @@ -14,11 +14,17 @@ package compute +import "yunion.io/x/onecloud/pkg/apis" + const ( - CDN_DOMAIN_STATUS_ONLINE = "online" - CDN_DOMAIN_STATUS_OFFLINE = "offline" - CDN_DOMAIN_STATUS_PROCESSING = "processing" - CDN_DOMAIN_STATUS_REJECTED = "rejected" + CDN_DOMAIN_STATUS_ONLINE = "online" + CDN_DOMAIN_STATUS_OFFLINE = "offline" + CDN_DOMAIN_STATUS_DELETING = "deleting" + CDN_DOMAIN_STATUS_DELETE_FAILED = "delete_failed" + CDN_DOMAIN_STATUS_PROCESSING = "processing" + CDN_DOMAIN_STATUS_REJECTED = "rejected" + CDN_DOMAIN_STATUS_UNKNOWN = "unknown" + CDN_DOMAIN_AREA_MAINLAND = "mainland" CDN_DOMAIN_AREA_OVERSEAS = "overseas" CDN_DOMAIN_AREA_GLOBAL = "global" @@ -45,3 +51,18 @@ type CdnDomain struct { type CdnDomains struct { Data []CdnDomain `json:"data"` } + +type CDNDomainCreateInput struct { +} + +type CDNDomainDetails struct { + apis.EnabledStatusInfrasResourceBaseDetails + ManagedResourceInfo +} + +type CDNDomainListInput struct { + apis.EnabledStatusInfrasResourceBaseListInput + apis.ExternalizedResourceBaseListInput + + ManagedResourceListInput +} diff --git a/pkg/cloudcommon/db/external.go b/pkg/cloudcommon/db/external.go index c2a13b8777..2d7ea0adb5 100644 --- a/pkg/cloudcommon/db/external.go +++ b/pkg/cloudcommon/db/external.go @@ -27,12 +27,13 @@ import ( // +onecloud:model-api-gen type SExternalizedResourceBase struct { - // 外部Id, 对用公有云私有资源自身的Id + // 云上Id, 对应云上资源自身Id ExternalId string `width:"256" charset:"utf8" index:"true" list:"user" create:"domain_optional" update:"admin" json:"external_id"` // 资源导入时间 ImportedAt time.Time `nullable:"true" created_at:"true" index:"true" get:"user" list:"user" json:"imported_at"` - Source string `width:"12" charset:"ascii" get:"user" list:"user" create:"optional" json:"source"` + // 资源来源, cloud: 从云上同步下来的资源, local: 从本地创建的资源或资源在本地更改过项目 + Source string `width:"12" charset:"ascii" get:"user" list:"user" create:"optional" json:"source"` } func (model *SExternalizedResourceBase) BeforeInsert() { diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index fcda09d2f6..3b76a87b31 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -294,6 +294,9 @@ type ICloudProvider interface { GetICloudInterVpcNetworks() ([]ICloudInterVpcNetwork, error) GetICloudInterVpcNetworkById(id string) (ICloudInterVpcNetwork, error) CreateICloudInterVpcNetwork(opts *SInterVpcNetworkCreateOptions) (ICloudInterVpcNetwork, error) + + GetICloudCDNDomains() ([]ICloudCDNDomain, error) + GetICloudCDNDomainByName(name string) (ICloudCDNDomain, error) } func IsSupportProject(prod ICloudProvider) bool { @@ -521,13 +524,23 @@ func (self *SBaseProvider) GetSamlSpInitiatedLoginUrl(idpName string) string { func (self *SBaseProvider) GetICloudInterVpcNetworks() ([]ICloudInterVpcNetwork, error) { return nil, ErrNotImplemented } + func (self *SBaseProvider) GetICloudInterVpcNetworkById(id string) (ICloudInterVpcNetwork, error) { return nil, ErrNotImplemented } + func (self *SBaseProvider) CreateICloudInterVpcNetwork(opts *SInterVpcNetworkCreateOptions) (ICloudInterVpcNetwork, error) { return nil, ErrNotImplemented } +func (self *SBaseProvider) GetICloudCDNDomains() ([]ICloudCDNDomain, error) { + return nil, errors.Wrapf(ErrNotImplemented, "GetICloudCDNDomains") +} + +func (self *SBaseProvider) GetICloudCDNDomainByName(name string) (ICloudCDNDomain, error) { + return nil, errors.Wrapf(ErrNotImplemented, "GetICloudCDNDomainByName") +} + func NewBaseProvider(factory ICloudProviderFactory) SBaseProvider { return SBaseProvider{factory: factory} } diff --git a/pkg/cloudprovider/consts.go b/pkg/cloudprovider/consts.go index aab33e96be..039faf9759 100644 --- a/pkg/cloudprovider/consts.go +++ b/pkg/cloudprovider/consts.go @@ -60,7 +60,8 @@ const ( CLOUD_CAPABILITY_MONGO_DB = "mongodb" // MongoDB CLOUD_CAPABILITY_ES = "es" // ElasticSearch CLOUD_CAPABILITY_KAFKA = "kafka" // Kafka - CLOUD_CAPABILITY_APP = "app" //App + CLOUD_CAPABILITY_APP = "app" // App + CLOUD_CAPABILITY_CDN = "cdn" // CDN ) const ( diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 13e478578f..f24459aebb 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -1497,3 +1497,12 @@ type ICloudNatSku interface { GetPrepaidStatus() string GetPostpaidStatus() string } + +type ICloudCDNDomain interface { + ICloudEnabledResource + + GetArea() string + GetServiceType() string + + Delete() error +} diff --git a/pkg/compute/models/cdn_domains.go b/pkg/compute/models/cdn_domains.go new file mode 100644 index 0000000000..011600a788 --- /dev/null +++ b/pkg/compute/models/cdn_domains.go @@ -0,0 +1,391 @@ +// 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" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/compare" + "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/db/lockman" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/onecloud/pkg/util/stringutils2" +) + +type SCDNDomainManager struct { + db.SEnabledStatusInfrasResourceBaseManager + db.SExternalizedResourceBaseManager + SManagedResourceBaseManager + SDeletePreventableResourceBaseManager +} + +var CDNDomainManager *SCDNDomainManager + +func init() { + CDNDomainManager = &SCDNDomainManager{ + SEnabledStatusInfrasResourceBaseManager: db.NewEnabledStatusInfrasResourceBaseManager( + SCDNDomain{}, + "cdn_domains_tbl", + "cdn_domain", + "cdn_domains", + ), + } + CDNDomainManager.SetVirtualObject(CDNDomainManager) +} + +type SCDNDomain struct { + db.SEnabledStatusInfrasResourceBase + db.SExternalizedResourceBase + + SDeletePreventableResourceBase + SManagedResourceBase + + // 服务类别 + ServiceType string `list:"user" width:"32" update:"admin" create:"admin_required"` + // 加速区域 + Area string `list:"user" width:"32" update:"admin" create:"admin_required"` +} + +func (manager *SCDNDomainManager) GetContextManagers() [][]db.IModelManager { + return [][]db.IModelManager{ + {CloudproviderManager}, + } +} + +func (manager *SCDNDomainManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []api.CDNDomainDetails { + rows := make([]api.CDNDomainDetails, len(objs)) + stdRows := manager.SEnabledStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + for i := range rows { + rows[i] = api.CDNDomainDetails{ + EnabledStatusInfrasResourceBaseDetails: stdRows[i], + ManagedResourceInfo: managerRows[i], + } + } + return rows +} + +func (self *SCloudprovider) GetCDNDomains() ([]SCDNDomain, error) { + q := CDNDomainManager.Query().Equals("manager_id", self.Id) + domains := []SCDNDomain{} + err := db.FetchModelObjects(CDNDomainManager, q, &domains) + if err != nil { + return nil, err + } + return domains, nil +} + +func (self *SCloudprovider) SyncCDNDomains(ctx context.Context, userCred mcclient.TokenCredential, exts []cloudprovider.ICloudCDNDomain) compare.SyncResult { + lockman.LockRawObject(ctx, CDNDomainManager.Keyword(), self.Id) + defer lockman.ReleaseRawObject(ctx, CDNDomainManager.Keyword(), self.Id) + + result := compare.SyncResult{} + + dbDomains, err := self.GetCDNDomains() + if err != nil { + result.Error(err) + return result + } + + removed := make([]SCDNDomain, 0) + commondb := make([]SCDNDomain, 0) + commonext := make([]cloudprovider.ICloudCDNDomain, 0) + added := make([]cloudprovider.ICloudCDNDomain, 0) + + err = compare.CompareSets(dbDomains, exts, &removed, &commondb, &commonext, &added) + if err != nil { + result.Error(err) + return result + } + + for i := 0; i < len(removed); i += 1 { + err = removed[i].syncRemoveCloudCDNDomain(ctx, userCred) + if err != nil { + result.DeleteError(err) + continue + } + result.Delete() + } + for i := 0; i < len(commondb); i += 1 { + err = commondb[i].SyncWithCloudCDNDomain(ctx, userCred, commonext[i]) + if err != nil { + result.UpdateError(err) + continue + } + result.Update() + } + for i := 0; i < len(added); i += 1 { + _, err := self.newFromCloudCDNDomain(ctx, userCred, added[i]) + if err != nil { + result.AddError(err) + continue + } + result.Add() + } + + return result +} + +func (self *SCDNDomain) syncRemoveCloudCDNDomain(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, self) + defer lockman.ReleaseObject(ctx, self) + + self.DeletePreventionOff(self, userCred) + + err := self.ValidateDeleteCondition(ctx) + if err != nil { + return errors.Wrapf(err, "ValidateDeleteCondition") + } + return self.RealDelete(ctx, userCred) +} + +func (self *SCDNDomain) GetICloudCDNDomain() (cloudprovider.ICloudCDNDomain, error) { + manager := self.GetCloudprovider() + if manager == nil { + return nil, errors.Wrapf(cloudprovider.ErrNotFound, "GetCloudprovider") + } + provider, err := manager.GetProvider() + if err != nil { + return nil, errors.Wrapf(err, "GetProvider") + } + return provider.GetICloudCDNDomainByName(self.Name) +} + +func (self *SCDNDomain) SyncWithCloudCDNDomain(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudCDNDomain) error { + diff, err := db.UpdateWithLock(ctx, self, func() error { + self.Name = ext.GetName() + self.Status = ext.GetStatus() + self.Area = ext.GetArea() + self.ServiceType = ext.GetServiceType() + return nil + }) + if err != nil { + return err + } + db.OpsLog.LogSyncUpdate(self, diff, userCred) + syncMetadata(ctx, userCred, self, ext) + + if provider := self.GetCloudprovider(); provider != nil { + SyncCloudDomain(userCred, self, provider.GetOwnerId()) + self.SyncShareState(ctx, userCred, provider.getAccountShareInfo()) + } + + return nil +} + +func (self *SCloudprovider) newFromCloudCDNDomain(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudCDNDomain) (*SCDNDomain, error) { + domain := SCDNDomain{} + domain.SetModelManager(CDNDomainManager, &domain) + + domain.ExternalId = ext.GetGlobalId() + domain.ManagerId = self.Id + domain.Name = ext.GetName() + domain.Status = ext.GetStatus() + domain.Area = ext.GetArea() + domain.ServiceType = ext.GetServiceType() + + err := CDNDomainManager.TableSpec().Insert(ctx, &domain) + if err != nil { + return nil, err + } + + syncMetadata(ctx, userCred, &domain, ext) + SyncCloudDomain(userCred, &domain, self.GetOwnerId()) + + domain.SyncShareState(ctx, userCred, self.getAccountShareInfo()) + + db.OpsLog.LogEvent(&domain, db.ACT_CREATE, domain.GetShortDesc(ctx), userCred) + + return &domain, nil +} + +func (manager *SCDNDomainManager) ValidateCreateData( + ctx context.Context, + userCred mcclient.TokenCredential, + ownerId mcclient.IIdentityProvider, + query jsonutils.JSONObject, + input api.CDNDomainCreateInput, +) (api.CDNDomainCreateInput, error) { + return input, nil +} + +func (self *SCDNDomain) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error { + + return self.StartDeleteTask(ctx, userCred, "") +} + +func (self *SCDNDomain) StartDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { + var err = func() error { + task, err := taskman.TaskManager.NewTask(ctx, "CDNDomainDeleteTask", self, userCred, nil, parentTaskId, "", nil) + if err != nil { + return errors.Wrapf(err, "NewTask") + } + return task.ScheduleRun(nil) + }() + if err != nil { + self.SetStatus(userCred, api.CDN_DOMAIN_STATUS_DELETE_FAILED, err.Error()) + return nil + } + return nil +} + +func (self *SCDNDomain) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { + self.SetStatus(userCred, api.CDN_DOMAIN_STATUS_DELETING, "") + return nil +} + +func (self *SCDNDomain) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error { + return self.SEnabledStatusInfrasResourceBase.Delete(ctx, userCred) +} + +// 列出CDN域名 +func (manager *SCDNDomainManager) ListItemFilter( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.CDNDomainListInput, +) (*sqlchemy.SQuery, error) { + var err error + + q, err = manager.SEnabledStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemFilter") + } + + q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter") + } + + q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput) + if err != nil { + return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter") + } + + return q, nil +} + +func (manager *SCDNDomainManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { + switch field { + default: + var err error + q, err = manager.SEnabledStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + + q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + } + return q, httperrors.ErrNotFound +} + +func (manager *SCDNDomainManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + query api.CDNDomainListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.EnabledStatusInfrasResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.OrderByExtraFields") + } + q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput) + if err != nil { + return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields") + } + return q, nil +} + +func (manager *SCDNDomainManager) totalCount( + ownerId mcclient.IIdentityProvider, + scope rbacutils.TRbacScope, + rangeObjs []db.IStandaloneModel, + providers []string, + brands []string, + cloudEnv string, +) int { + q := CDNDomainManager.Query() + + if scope != rbacutils.ScopeSystem && ownerId != nil { + q = q.Equals("domain_id", ownerId.GetProjectDomainId()) + } + q = CloudProviderFilter(q, q.Field("manager_id"), providers, brands, cloudEnv) + q = RangeObjectsFilter(q, rangeObjs, q.Field("cloudregion_id"), nil, q.Field("manager_id"), nil, nil) + + cnt, _ := q.CountWithError() + + return cnt +} + +func (manager *SCDNDomainManager) ListItemExportKeys(ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + keys stringutils2.SSortedStrings, +) (*sqlchemy.SQuery, error) { + q, err := manager.SEnabledStatusInfrasResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBaseManager.ListItemExportKeys") + } + if keys.ContainsAny(manager.SManagedResourceBaseManager.GetExportKeys()...) { + q, err = manager.SManagedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemExportKeys") + } + } + + return q, nil +} + +func (self *SCDNDomain) AllowPerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, self, "syncstatus") +} + +// 同步域名状态 +func (self *SCDNDomain) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.NatGatewaySyncstatusInput) (jsonutils.JSONObject, error) { + var openTask = true + count, err := taskman.TaskManager.QueryTasksOfObject(self, time.Now().Add(-3*time.Minute), &openTask).CountWithError() + if err != nil { + return nil, err + } + if count > 0 { + return nil, httperrors.NewBadRequestError("CDN domain has %d task active, can't sync status", count) + } + + return nil, self.StartSyncstatus(ctx, userCred, "") +} + +func (self *SCDNDomain) StartSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error { + return StartResourceSyncStatusTask(ctx, userCred, self, "CDNDomainSyncstatusTask", parentTaskId) +} diff --git a/pkg/compute/service/handlers.go b/pkg/compute/service/handlers.go index 84b0bbd30d..9a6ccd05ff 100644 --- a/pkg/compute/service/handlers.go +++ b/pkg/compute/service/handlers.go @@ -234,6 +234,8 @@ func InitHandlers(app *appsrv.Application) { models.AppManager, models.AppEnvironmentManager, + + models.CDNDomainManager, } { db.RegisterModelManager(manager) handler := db.NewModelHandler(manager) diff --git a/pkg/compute/tasks/cdn_domain_delete_task.go b/pkg/compute/tasks/cdn_domain_delete_task.go new file mode 100644 index 0000000000..067f3263a6 --- /dev/null +++ b/pkg/compute/tasks/cdn_domain_delete_task.go @@ -0,0 +1,72 @@ +// 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" + "database/sql" + + "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/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type CDNDomainDeleteTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(CDNDomainDeleteTask{}) +} + +func (self *CDNDomainDeleteTask) taskFailed(ctx context.Context, domain *models.SCDNDomain, err error) { + domain.SetStatus(self.UserCred, api.CDN_DOMAIN_STATUS_DELETE_FAILED, err.Error()) + db.OpsLog.LogEvent(domain, db.ACT_DELOCATE_FAIL, err.Error(), self.UserCred) + logclient.AddActionLogWithStartable(self, domain, logclient.ACT_DELETE, err.Error(), self.UserCred, false) + self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) +} + +func (self *CDNDomainDeleteTask) taskComplete(ctx context.Context, domain *models.SCDNDomain) { + domain.RealDelete(ctx, self.GetUserCred()) + self.SetStageComplete(ctx, nil) +} + +func (self *CDNDomainDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { + domain := obj.(*models.SCDNDomain) + + iDomain, err := domain.GetICloudCDNDomain() + if err != nil { + if errors.Cause(err) == cloudprovider.ErrNotFound || errors.Cause(err) == sql.ErrNoRows { + self.taskComplete(ctx, domain) + return + } + self.taskFailed(ctx, domain, errors.Wrapf(err, "GetICloudCDNDomain")) + return + } + + err = iDomain.Delete() + if err != nil { + self.taskFailed(ctx, domain, errors.Wrapf(err, "Delete")) + return + } + + self.taskComplete(ctx, domain) +} diff --git a/pkg/compute/tasks/cdn_domain_syncstatus_task.go b/pkg/compute/tasks/cdn_domain_syncstatus_task.go new file mode 100644 index 0000000000..1828a8bdfb --- /dev/null +++ b/pkg/compute/tasks/cdn_domain_syncstatus_task.go @@ -0,0 +1,61 @@ +// 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" + "yunion.io/x/onecloud/pkg/util/logclient" +) + +type CDNDomainSyncstatusTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(CDNDomainSyncstatusTask{}) +} + +func (self *CDNDomainSyncstatusTask) taskFailed(ctx context.Context, domain *models.SCDNDomain, err error) { + domain.SetStatus(self.GetUserCred(), api.CDN_DOMAIN_STATUS_UNKNOWN, err.Error()) + db.OpsLog.LogEvent(domain, db.ACT_SYNC_STATUS, domain.GetShortDesc(ctx), self.GetUserCred()) + logclient.AddActionLogWithContext(ctx, domain, logclient.ACT_SYNC_STATUS, err, self.UserCred, false) + self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) +} + +func (self *CDNDomainSyncstatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + domain := obj.(*models.SCDNDomain) + + iDomain, err := domain.GetICloudCDNDomain() + if err != nil { + self.taskFailed(ctx, domain, errors.Wrapf(err, "GetICloudCDNDomain")) + return + } + + err = domain.SyncWithCloudCDNDomain(ctx, self.GetUserCred(), iDomain) + if err != nil { + self.taskFailed(ctx, domain, errors.Wrapf(err, "SyncWithCloudCDNDomain")) + return + } + + self.SetStageComplete(ctx, nil) +} diff --git a/pkg/compute/tasks/cloud_provider_sync_info_task.go b/pkg/compute/tasks/cloud_provider_sync_info_task.go index ca6f4a1e5e..f33caa0d8b 100644 --- a/pkg/compute/tasks/cloud_provider_sync_info_task.go +++ b/pkg/compute/tasks/cloud_provider_sync_info_task.go @@ -88,6 +88,11 @@ func (self *CloudProviderSyncInfoTask) OnInit(ctx context.Context, obj db.IStand notes := fmt.Sprintf("SyncQuotas for provider %s result: %s", provider.Name, msg) log.Infof(notes) } + domains, err := p.GetICloudCDNDomains() + if err == nil { + result := provider.SyncCDNDomains(ctx, self.GetUserCred(), domains) + log.Infof("Sync CDN for provider %s result: %s", provider.Name, result.Result()) + } return nil, nil }) } diff --git a/pkg/mcclient/modules/mod_cdn_domains.go b/pkg/mcclient/modules/mod_cdn_domains.go new file mode 100644 index 0000000000..c32e65b2dd --- /dev/null +++ b/pkg/mcclient/modules/mod_cdn_domains.go @@ -0,0 +1,36 @@ +// 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 modules + +import ( + "yunion.io/x/onecloud/pkg/mcclient/modulebase" +) + +type SCDNDomainManager struct { + modulebase.ResourceManager +} + +var ( + CDNDomains SCDNDomainManager +) + +func init() { + CDNDomains = SCDNDomainManager{ + NewComputeManager("cdn_domain", "cdn_domains", + []string{}, + []string{}), + } + registerCompute(&CDNDomains) +} diff --git a/pkg/mcclient/options/compute/cdn_domains.go b/pkg/mcclient/options/compute/cdn_domains.go new file mode 100644 index 0000000000..1203a67030 --- /dev/null +++ b/pkg/mcclient/options/compute/cdn_domains.go @@ -0,0 +1,29 @@ +// 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 CDNDomainListOptions struct { + options.BaseListOptions +} + +func (opts *CDNDomainListOptions) Params() (jsonutils.JSONObject, error) { + return options.StructToParams(opts) +} diff --git a/pkg/multicloud/qcloud/cdn.go b/pkg/multicloud/qcloud/cdn.go index 6b69eb2deb..e09e9c2a6d 100644 --- a/pkg/multicloud/qcloud/cdn.go +++ b/pkg/multicloud/qcloud/cdn.go @@ -18,8 +18,10 @@ import ( "fmt" "strconv" - "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" ) type SCdnOrigin struct { @@ -32,7 +34,13 @@ type SCdnOrigin struct { BackupOriginType interface{} `json:"BackupOriginType"` BackupServerName interface{} `json:"BackupServerName"` } + type SCdnDomain struct { + multicloud.SResourceBase + multicloud.QcloudTags + + client *SQcloudClient + Area string `json:"Area"` Cname string `json:"Cname"` CreateTime string `json:"CreateTime"` @@ -46,12 +54,80 @@ type SCdnDomain struct { Status string `json:"Status"` UpdateTime string `json:"UpdateTime"` } + +func (self *SCdnDomain) GetName() string { + return self.Domain +} + +func (self *SCdnDomain) GetGlobalId() string { + return self.Domain +} + +func (self *SCdnDomain) GetId() string { + return self.Domain +} + +func (self *SCdnDomain) GetStatus() string { + return self.Status +} + +func (self *SCdnDomain) GetEnabled() bool { + return self.Disable == "normal" +} + +func (self *SCdnDomain) GetArea() string { + return self.Area +} + +func (self *SCdnDomain) GetServiceType() string { + return self.ServiceType +} + +func (self *SCdnDomain) Delete() error { + return self.client.DeleteCdnDomain(self.Domain) +} + +func (self *SQcloudClient) DeleteCdnDomain(domain string) error { + params := map[string]string{ + "Domain": domain, + } + _, err := self.cdnRequest("DeleteCdnDomain", params) + return errors.Wrapf(err, "DeleteCdnDomain") +} + type SDomains struct { RequestID string `json:"RequestId"` Domains []SCdnDomain `json:"Domains"` TotalNumber int `json:"TotalNumber"` } +func (self *SQcloudClient) GetICloudCDNDomains() ([]cloudprovider.ICloudCDNDomain, error) { + cdns, err := self.DescribeAllCdnDomains(nil, nil, "") + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudCDNDomain{} + for i := range cdns { + cdns[i].client = self + ret = append(ret, &cdns[i]) + } + return ret, nil +} + +func (self *SQcloudClient) GetICloudCDNDomainByName(name string) (cloudprovider.ICloudCDNDomain, error) { + domains, _, err := self.DescribeCdnDomains([]string{name}, nil, "", 0, 1) + if err != nil { + return nil, errors.Wrapf(err, "DescribeCdnDomains") + } + for i := range domains { + if domains[i].Domain == name { + domains[i].client = self + return &domains[i], nil + } + } + return nil, errors.Wrapf(cloudprovider.ErrNotFound, name) +} + func (client *SQcloudClient) AddCdnDomain(domain string, originType string, origins []string, cosPrivateAccess string) error { params := map[string]string{} params["Domain"] = domain @@ -63,7 +139,7 @@ func (client *SQcloudClient) AddCdnDomain(domain string, originType string, orig params["Origin.CosPrivateAccess"] = cosPrivateAccess _, err := client.cdnRequest("AddCdnDomain", params) if err != nil { - return errors.Wrapf(err, ` client.cdnRequest("AddCdnDomain", %s)`, jsonutils.Marshal(params).String()) + return errors.Wrapf(err, `AddCdnDomain %s`, params) } return nil } @@ -96,12 +172,12 @@ func (client *SQcloudClient) DescribeCdnDomains(domains, origins []string, domai resp, err := client.cdnRequest("DescribeDomains", params) if err != nil { - return nil, 0, errors.Wrapf(err, "client.DescribeDomains(DescribeDomains, %s)", jsonutils.Marshal(params).String()) + return nil, 0, errors.Wrapf(err, "DescribeDomains %s", params) } cdnDomains := []SCdnDomain{} err = resp.Unmarshal(&cdnDomains, "Domains") if err != nil { - return nil, 0, errors.Wrapf(err, "%s.Unmarshal(records)", jsonutils.Marshal(resp).String()) + return nil, 0, errors.Wrapf(err, "resp.Unmarshal") } totalcount, _ := resp.Float("TotalNumber") return cdnDomains, int(totalcount), nil @@ -112,7 +188,7 @@ func (client *SQcloudClient) DescribeAllCdnDomains(domains, origins []string, do for { part, total, err := client.DescribeCdnDomains(domains, origins, domainType, len(cdnDomains), 50) if err != nil { - return nil, errors.Wrap(err, "client.DescribeCdnDomains(domains, origins, len(cdnDomains), 50)") + return nil, errors.Wrap(err, "DescribeCdnDomains") } cdnDomains = append(cdnDomains, part...) if len(cdnDomains) >= total { diff --git a/pkg/multicloud/qcloud/provider/provider.go b/pkg/multicloud/qcloud/provider/provider.go index d5ed2e0a70..b404b2092a 100644 --- a/pkg/multicloud/qcloud/provider/provider.go +++ b/pkg/multicloud/qcloud/provider/provider.go @@ -441,3 +441,11 @@ func (self *SQcloudProvider) GetICloudInterVpcNetworkById(id string) (cloudprovi func (self *SQcloudProvider) CreateICloudInterVpcNetwork(opts *cloudprovider.SInterVpcNetworkCreateOptions) (cloudprovider.ICloudInterVpcNetwork, error) { return self.client.CreateICloudInterVpcNetwork(opts) } + +func (self *SQcloudProvider) GetICloudCDNDomains() ([]cloudprovider.ICloudCDNDomain, error) { + return self.client.GetICloudCDNDomains() +} + +func (self *SQcloudProvider) GetICloudCDNDomainByName(name string) (cloudprovider.ICloudCDNDomain, error) { + return self.client.GetICloudCDNDomainByName(name) +} diff --git a/pkg/multicloud/qcloud/qcloud.go b/pkg/multicloud/qcloud/qcloud.go index bf56a5a4ff..b8ec4d10a6 100644 --- a/pkg/multicloud/qcloud/qcloud.go +++ b/pkg/multicloud/qcloud/qcloud.go @@ -1089,8 +1089,6 @@ func (self *SQcloudClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_LOADBALANCER, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE, cloudprovider.CLOUD_CAPABILITY_RDS, - // cloudprovider.CLOUD_CAPABILITY_CACHE, - // cloudprovider.CLOUD_CAPABILITY_RDS, cloudprovider.CLOUD_CAPABILITY_CACHE, cloudprovider.CLOUD_CAPABILITY_EVENT, cloudprovider.CLOUD_CAPABILITY_CLOUDID, @@ -1101,6 +1099,7 @@ func (self *SQcloudClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_MONGO_DB, cloudprovider.CLOUD_CAPABILITY_ES, cloudprovider.CLOUD_CAPABILITY_KAFKA, + cloudprovider.CLOUD_CAPABILITY_CDN, } return caps }