diff --git a/cmd/climc/shell/external_projects.go b/cmd/climc/shell/external_projects.go new file mode 100644 index 0000000000..409cd5e5f1 --- /dev/null +++ b/cmd/climc/shell/external_projects.go @@ -0,0 +1,44 @@ +package shell + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +func init() { + type ExternalProjectListOptions struct { + options.BaseListOptions + } + R(&ExternalProjectListOptions{}, "external-project-list", "List public cloud projects", func(s *mcclient.ClientSession, opts *ExternalProjectListOptions) error { + params, err := options.ListStructToParams(opts) + if err != nil { + return err + } + + result, err := modules.ExternalProjects.List(s, params) + if err != nil { + return err + } + printList(result, modules.Disks.GetColumns(s)) + return nil + }) + + type ExternalProjectUpdateOptions struct { + ID string `help:"ExternalProject ID or Name"` + PROJECT string `help:"Local project ID or Name"` + } + + R(&ExternalProjectUpdateOptions{}, "external-project-update", "Update external project point to local project", func(s *mcclient.ClientSession, args *ExternalProjectUpdateOptions) error { + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(args.PROJECT), "project") + result, err := modules.ExternalProjects.Update(s, args.ID, params) + if err != nil { + return err + } + printObject(result) + return nil + }) + +} diff --git a/pkg/cloudcommon/db/opslog.go b/pkg/cloudcommon/db/opslog.go index 0ffee3eb56..9177bd8743 100644 --- a/pkg/cloudcommon/db/opslog.go +++ b/pkg/cloudcommon/db/opslog.go @@ -127,6 +127,8 @@ const ( ACT_SYNCING_HOST = "syncing_host" ACT_SYNC_HOST_COMPLETE = "sync_host_end" + ACT_SYNC_PROJECT_COMPLETE = "sync_project_end" + ACT_SYNC_LB_START = "sync_lb_start" ACT_SYNCING_LB = "syncing_lb" ACT_SYNC_LB_COMPLETE = "sync_lb_end" @@ -138,9 +140,10 @@ const ( ACT_UNCACHE_IMAGE_FAIL = "uncache_image_fail" ACT_UNCACHED_IMAGE = "uncached_image" - ACT_SYNC_CLOUD_DISK = "sync_cloud_disk" - ACT_SYNC_CLOUD_SERVER = "sync_cloud_server" - ACT_SYNC_CLOUD_EIP = "sync_cloud_eip" + ACT_SYNC_CLOUD_DISK = "sync_cloud_disk" + ACT_SYNC_CLOUD_SERVER = "sync_cloud_server" + ACT_SYNC_CLOUD_EIP = "sync_cloud_eip" + ACT_SYNC_CLOUD_PROJECT = "sync_cloud_project" ACT_PENDING_DELETE = "pending_delete" ACT_CANCEL_DELETE = "cancel_delete" diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index 1e3465e31c..24a22b000a 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -34,6 +34,7 @@ type ICloudProviderFactory interface { IsOnPremise() bool IsSupportPrepaidResources() bool NeedSyncSkuFromCloud() bool + IsProjectRegional() bool } type ICloudProvider interface { diff --git a/pkg/cloudprovider/fakeregion.go b/pkg/cloudprovider/fakeregion.go index c18523f6a6..d40c15df2f 100644 --- a/pkg/cloudprovider/fakeregion.go +++ b/pkg/cloudprovider/fakeregion.go @@ -119,3 +119,7 @@ func (region *SFakeOnPremiseRegion) CreateILoadBalancerAcl(acl *SLoadbalancerAcc func (region *SFakeOnPremiseRegion) GetSkus(zoneId string) ([]ICloudSku, error) { return nil, ErrNotSupported } + +func (region *SFakeOnPremiseRegion) GetProjects() ([]ICloudProject, error) { + return nil, ErrNotSupported +} diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 9c2e7fadae..049e11f5c5 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -77,6 +77,8 @@ type ICloudRegion interface { GetSkus(zoneId string) ([]ICloudSku, error) GetProvider() string + + GetProjects() ([]ICloudProject, error) } type ICloudZone interface { @@ -561,3 +563,7 @@ type ICloudSku interface { GetGpuCount() int GetGpuMaxCount() int } + +type ICloudProject interface { + ICloudResource +} diff --git a/pkg/compute/models/external_projects.go b/pkg/compute/models/external_projects.go new file mode 100644 index 0000000000..f2042d9a88 --- /dev/null +++ b/pkg/compute/models/external_projects.go @@ -0,0 +1,200 @@ +package models + +import ( + "context" + "database/sql" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/options" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/auth" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/pkg/util/compare" +) + +type SExternalProjectManager struct { + db.SStandaloneResourceBaseManager +} + +var ExternalProjectManager *SExternalProjectManager + +func init() { + ExternalProjectManager = &SExternalProjectManager{ + SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager( + SExternalProject{}, + "externalprojects_tbl", + "externalproject", + "externalprojects", + ), + } +} + +type SExternalProject struct { + db.SStandaloneResourceBase + SManagedResourceBase + + ProjectId string `width:"128" charset:"ascii" nullable:"true" list:"admin" update:"admin"` + CloudregionId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"optional"` +} + +func (self *SExternalProject) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + if project := jsonutils.GetAnyString(data, []string{"project_id", "project", "tenant_id", "tenant"}); len(project) > 0 { + _project, err := db.TenantCacheManager.FetchByIdOrName(userCred, project) + if err != nil { + if err == sql.ErrNoRows { + return nil, httperrors.NewTenantNotFoundError("project %s not find", project) + } + return nil, err + } + data.Set("project_id", jsonutils.NewString(_project.GetId())) + } + return self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) +} + +func (manager *SExternalProjectManager) getProjectsByRegion(region *SCloudregion, provider *SCloudprovider) ([]SExternalProject, error) { + projects := []SExternalProject{} + factory, err := provider.GetProviderFactory() + if err != nil { + return nil, err + } + q := manager.Query() + if factory.IsProjectRegional() { + q = q.Equals("cloudregion_id", region.Id) + } + if provider != nil { + q = q.Equals("manager_id", provider.Id) + } + err = db.FetchModelObjects(manager, q, &projects) + if err != nil { + return nil, err + } + return projects, nil +} + +func (manager *SExternalProjectManager) SyncProjects(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, region *SCloudregion, projects []cloudprovider.ICloudProject, projectSync bool) compare.SyncResult { + syncResult := compare.SyncResult{} + + dbProjects, err := manager.getProjectsByRegion(region, provider) + if err != nil { + syncResult.Error(err) + return syncResult + } + + removed := make([]SExternalProject, 0) + commondb := make([]SExternalProject, 0) + commonext := make([]cloudprovider.ICloudProject, 0) + added := make([]cloudprovider.ICloudProject, 0) + + err = compare.CompareSets(dbProjects, projects, &removed, &commondb, &commonext, &added) + if err != nil { + syncResult.Error(err) + return syncResult + } + + for i := 0; i < len(removed); i++ { + err = removed[i].Delete(ctx, userCred) + if err != nil { + syncResult.DeleteError(err) + } else { + syncResult.Delete() + } + } + for i := 0; i < len(commondb); i++ { + err = commondb[i].SyncWithCloudProject(ctx, userCred, provider, commonext[i], projectSync) + if err != nil { + syncResult.UpdateError(err) + } else { + syncResult.Update() + } + } + for i := 0; i < len(added); i++ { + _, err := manager.newFromCloudProject(ctx, userCred, provider, added[i], region, projectSync) + if err != nil { + syncResult.AddError(err) + } else { + syncResult.Add() + } + } + return syncResult +} + +func (self *SExternalProject) SyncWithCloudProject(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, ext cloudprovider.ICloudProject, projectSync bool) error { + _, err := self.GetModelManager().TableSpec().Update(self, func() error { + self.Name = ext.GetName() + self.ExternalId = ext.GetGlobalId() + self.IsEmulated = ext.IsEmulated() + self.ProjectId = userCred.GetProjectId() + if projectSync { + projectId, err := ExternalProjectManager.syncProject(ctx, userCred, self.Name) + if err != nil { + return err + } + self.ProjectId = projectId + } + return nil + }) + if err != nil { + log.Errorf("SyncWithCloudProject fail %s", err) + } + return err +} + +func (manager *SExternalProjectManager) syncProject(ctx context.Context, userCred mcclient.TokenCredential, projectName string) (string, error) { + project, err := db.TenantCacheManager.FetchByName(userCred, projectName) + if err == nil { + return project.GetId(), nil + } + if err == sql.ErrNoRows { + s := auth.GetAdminSession(ctx, options.Options.Region, "") + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(projectName), "name") + params.Add(jsonutils.NewString("auto create from external_project"), "description") + result, err := modules.Projects.Create(s, params) + if err != nil { + return "", err + } + return result.GetString("id") + } + return "", err +} + +func (manager *SExternalProjectManager) newFromCloudProject(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, extProject cloudprovider.ICloudProject, region *SCloudregion, projectSync bool) (*SExternalProject, error) { + project := SExternalProject{} + project.SetModelManager(manager) + + project.Name = extProject.GetName() + project.ExternalId = extProject.GetGlobalId() + project.IsEmulated = extProject.IsEmulated() + project.ManagerId = provider.Id + + project.ProjectId = userCred.GetProjectId() + if projectSync { + projectId, err := manager.syncProject(ctx, userCred, project.Name) + if err != nil { + return nil, err + } + project.ProjectId = projectId + } + + factory, err := provider.GetProviderFactory() + if err != nil { + return nil, err + } + + if factory.IsProjectRegional() { + project.CloudregionId = region.Id + } + + err = manager.TableSpec().Insert(&project) + if err != nil { + log.Errorf("newFromCloudProject fail %s", err) + return nil, err + } + + db.OpsLog.LogEvent(&project, db.ACT_SYNC_CLOUD_PROJECT, project.GetShortDesc(ctx), userCred) + return &project, nil +} diff --git a/pkg/compute/service/handlers.go b/pkg/compute/service/handlers.go index dd2462f5b7..80539956d2 100644 --- a/pkg/compute/service/handlers.go +++ b/pkg/compute/service/handlers.go @@ -81,6 +81,7 @@ func InitHandlers(app *appsrv.Application) { models.DynamicschedtagManager, models.ServerSkuManager, + models.ExternalProjectManager, } { db.RegisterModelManager(manager) handler := db.NewModelHandler(manager) diff --git a/pkg/compute/tasks/cloud_provider_sync_info_task.go b/pkg/compute/tasks/cloud_provider_sync_info_task.go index 852d01468f..b30a37c818 100644 --- a/pkg/compute/tasks/cloud_provider_sync_info_task.go +++ b/pkg/compute/tasks/cloud_provider_sync_info_task.go @@ -155,6 +155,10 @@ func syncPublicCloudProviderInfo(ctx context.Context, provider *models.SCloudpro if len(syncRange.Region) > 0 && !utils.IsInStringArray(localRegions[i].Id, syncRange.Region) { continue } + if driver.GetFactory().IsProjectRegional() || i == 0 { + syncRegionProjects(ctx, provider, task, &localRegions[i], remoteRegions[i], syncRange) + } + syncRegionEips(ctx, provider, task, &localRegions[i], remoteRegions[i], syncRange) localZones, remoteZones := syncRegionZones(ctx, provider, task, &localRegions[i], remoteRegions[i]) @@ -391,6 +395,25 @@ func syncRegionSnapshots(ctx context.Context, provider *models.SCloudprovider, t db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, task.GetUserCred()) } +func syncRegionProjects(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localRegion *models.SCloudregion, remoteRegion cloudprovider.ICloudRegion, syncRange *models.SSyncRange) { + projects, err := remoteRegion.GetProjects() + if err != nil { + msg := fmt.Sprintf("GetIProjects for region %s failed %s", remoteRegion.GetName(), err) + log.Errorf(msg) + logSyncFailed(provider, task, msg) + return + } + + result := models.ExternalProjectManager.SyncProjects(ctx, task.UserCred, provider, localRegion, projects, syncRange.ProjectSync) + msg := result.Result() + log.Infof("SyncProjects for region %s result: %s", localRegion.Name, msg) + if result.IsError() { + logSyncFailed(provider, task, msg) + return + } + db.OpsLog.LogEvent(provider, db.ACT_SYNC_PROJECT_COMPLETE, msg, task.UserCred) +} + func syncRegionEips(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localRegion *models.SCloudregion, remoteRegion cloudprovider.ICloudRegion, syncRange *models.SSyncRange) { eips, err := remoteRegion.GetIEips() if err != nil { diff --git a/pkg/mcclient/modules/mod_externalprojects.go b/pkg/mcclient/modules/mod_externalprojects.go new file mode 100644 index 0000000000..0d0c1d177d --- /dev/null +++ b/pkg/mcclient/modules/mod_externalprojects.go @@ -0,0 +1,13 @@ +package modules + +var ( + ExternalProjects ResourceManager +) + +func init() { + ExternalProjects = NewComputeManager("externalproject", "externalprojects", + []string{"ID", "Name", "ExternalId", "Created_at", "CloudregionId", "ProjectId"}, + []string{}) + + registerComputeV2(&ExternalProjects) +} diff --git a/pkg/util/aliyun/project.go b/pkg/util/aliyun/project.go new file mode 100644 index 0000000000..ea03a4fbd0 --- /dev/null +++ b/pkg/util/aliyun/project.go @@ -0,0 +1,12 @@ +package aliyun + +import "time" + +type SProject struct { + Status string + AccountId string + DisplayName string + Id string + CreateDate time.Time + Name string +} diff --git a/pkg/util/aliyun/provider/provider.go b/pkg/util/aliyun/provider/provider.go index 3a23c62047..f810f4edbe 100644 --- a/pkg/util/aliyun/provider/provider.go +++ b/pkg/util/aliyun/provider/provider.go @@ -34,6 +34,10 @@ func (self *SAliyunProviderFactory) IsOnPremise() bool { return false } +func (self *SAliyunProviderFactory) IsProjectRegional() bool { + return false +} + func (self *SAliyunProviderFactory) IsSupportPrepaidResources() bool { return true } diff --git a/pkg/util/aliyun/region.go b/pkg/util/aliyun/region.go index a558d1f763..7ab90ac8c1 100644 --- a/pkg/util/aliyun/region.go +++ b/pkg/util/aliyun/region.go @@ -893,3 +893,10 @@ func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAc func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) { return nil, cloudprovider.ErrNotImplemented } + +func (region *SRegion) GetProjects() ([]cloudprovider.ICloudProject, error) { + // 阿里云并未公布资源组的api地址 + // params := map[string]string{} + // body, err := region.ecsRequest("ListResourceGroups", params) + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/aliyun/shell/project.go b/pkg/util/aliyun/shell/project.go new file mode 100644 index 0000000000..3f73e4c147 --- /dev/null +++ b/pkg/util/aliyun/shell/project.go @@ -0,0 +1,19 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/aliyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type ProjectListOptions struct { + } + shellutils.R(&ProjectListOptions{}, "project-list", "List project", func(cli *aliyun.SRegion, args *ProjectListOptions) error { + project, err := cli.GetProjects() + if err != nil { + return err + } + printList(project, 0, 0, 0, nil) + return nil + }) +} diff --git a/pkg/util/aws/provider/provider.go b/pkg/util/aws/provider/provider.go index cf9a3c543f..a7c8de4afe 100644 --- a/pkg/util/aws/provider/provider.go +++ b/pkg/util/aws/provider/provider.go @@ -37,6 +37,10 @@ func (self *SAwsProviderFactory) IsSupportPrepaidResources() bool { return true } +func (self *SAwsProviderFactory) IsProjectRegional() bool { + return false +} + func (self *SAwsProviderFactory) NeedSyncSkuFromCloud() bool { return false } diff --git a/pkg/util/aws/region.go b/pkg/util/aws/region.go index 7cdab4638b..097228f783 100644 --- a/pkg/util/aws/region.go +++ b/pkg/util/aws/region.go @@ -503,3 +503,7 @@ func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAc func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) { return nil, cloudprovider.ErrNotImplemented } + +func (region *SRegion) GetProjects() ([]cloudprovider.ICloudProject, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/azure/provider/provider.go b/pkg/util/azure/provider/provider.go index 936827e17c..aca95f092f 100644 --- a/pkg/util/azure/provider/provider.go +++ b/pkg/util/azure/provider/provider.go @@ -39,6 +39,10 @@ func (self *SAzureProviderFactory) IsSupportPrepaidResources() bool { return true } +func (self *SAzureProviderFactory) IsProjectRegional() bool { + return false +} + func (self *SAzureProviderFactory) NeedSyncSkuFromCloud() bool { return false } diff --git a/pkg/util/azure/region.go b/pkg/util/azure/region.go index 3eef898d28..f8d4379e87 100644 --- a/pkg/util/azure/region.go +++ b/pkg/util/azure/region.go @@ -555,3 +555,15 @@ func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAc func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) { return nil, cloudprovider.ErrNotImplemented } + +func (region *SRegion) GetProjects() ([]cloudprovider.ICloudProject, error) { + resourceGroups, err := region.GetResourceGroups() + if err != nil { + return nil, err + } + iprojects := []cloudprovider.ICloudProject{} + for i := 0; i < len(resourceGroups); i++ { + iprojects = append(iprojects, &resourceGroups[i]) + } + return iprojects, nil +} diff --git a/pkg/util/azure/resourcegroup.go b/pkg/util/azure/resourcegroup.go index c9589ef0bf..f1fccd2960 100644 --- a/pkg/util/azure/resourcegroup.go +++ b/pkg/util/azure/resourcegroup.go @@ -30,3 +30,31 @@ func (self *SRegion) CreateResourceGroup(groupName string) (*SResourceGroup, err resourceGroup := SResourceGroup{Name: groupName, Location: self.Name} return &resourceGroup, self.client.Create(jsonutils.Marshal(resourceGroup), &resourceGroup) } + +func (r *SResourceGroup) GetName() string { + return r.Name +} + +func (r *SResourceGroup) GetId() string { + return r.ID +} + +func (r *SResourceGroup) GetGlobalId() string { + return r.ID +} + +func (r *SResourceGroup) GetStatus() string { + return "" +} + +func (r *SResourceGroup) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (r *SResourceGroup) IsEmulated() bool { + return false +} + +func (r *SResourceGroup) Refresh() error { + return nil +} diff --git a/pkg/util/esxi/provider/provider.go b/pkg/util/esxi/provider/provider.go index aece6af9f1..ef70f2301c 100644 --- a/pkg/util/esxi/provider/provider.go +++ b/pkg/util/esxi/provider/provider.go @@ -39,6 +39,10 @@ func (self *SESXiProviderFactory) IsOnPremise() bool { return true } +func (self *SESXiProviderFactory) IsProjectRegional() bool { + return false +} + func (self *SESXiProviderFactory) IsSupportPrepaidResources() bool { return false } diff --git a/pkg/util/huawei/provider/provider.go b/pkg/util/huawei/provider/provider.go index 532f05b00a..cd46cb08fd 100644 --- a/pkg/util/huawei/provider/provider.go +++ b/pkg/util/huawei/provider/provider.go @@ -37,6 +37,10 @@ func (self *SHuaweiProviderFactory) IsSupportPrepaidResources() bool { return true } +func (self *SHuaweiProviderFactory) IsProjectRegional() bool { + return true +} + func (self *SHuaweiProviderFactory) NeedSyncSkuFromCloud() bool { return false } diff --git a/pkg/util/huawei/region.go b/pkg/util/huawei/region.go index 62a49486a3..5f3207cd62 100644 --- a/pkg/util/huawei/region.go +++ b/pkg/util/huawei/region.go @@ -649,3 +649,7 @@ func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAc func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) { return nil, cloudprovider.ErrNotImplemented } + +func (region *SRegion) GetProjects() ([]cloudprovider.ICloudProject, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/openstack/provider/provider.go b/pkg/util/openstack/provider/provider.go index 67771e2f51..ed57ea2b1d 100644 --- a/pkg/util/openstack/provider/provider.go +++ b/pkg/util/openstack/provider/provider.go @@ -44,6 +44,10 @@ func (self *SOpenStackProviderFactory) IsSupportPrepaidResources() bool { return false } +func (self *SOpenStackProviderFactory) IsProjectRegional() bool { + return false +} + func (self *SOpenStackProviderFactory) NeedSyncSkuFromCloud() bool { return true } diff --git a/pkg/util/openstack/region.go b/pkg/util/openstack/region.go index 71aa555326..2bfff75d02 100644 --- a/pkg/util/openstack/region.go +++ b/pkg/util/openstack/region.go @@ -439,3 +439,7 @@ func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) } return iskus, nil } + +func (region *SRegion) GetProjects() ([]cloudprovider.ICloudProject, error) { + return nil, cloudprovider.ErrNotImplemented +} diff --git a/pkg/util/qcloud/project.go b/pkg/util/qcloud/project.go new file mode 100644 index 0000000000..af08704020 --- /dev/null +++ b/pkg/util/qcloud/project.go @@ -0,0 +1,43 @@ +package qcloud + +import ( + "time" + + "yunion.io/x/jsonutils" +) + +type SProject struct { + ProjectName string `json:"projectName"` + ProjectId string `json:projectId` + CreateTime time.Time `json:createTime` + CreateorUin int `json:"creatorUin"` + ProjectInfo string `json:"projectInfo"` +} + +func (p *SProject) GetId() string { + return p.ProjectId +} + +func (p *SProject) GetGlobalId() string { + return p.ProjectId +} + +func (p *SProject) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (p *SProject) GetName() string { + return p.ProjectName +} + +func (p *SProject) GetStatus() string { + return "" +} + +func (p *SProject) IsEmulated() bool { + return false +} + +func (p *SProject) Refresh() error { + return nil +} diff --git a/pkg/util/qcloud/provider/provider.go b/pkg/util/qcloud/provider/provider.go index d7bbf95255..acd945af2d 100644 --- a/pkg/util/qcloud/provider/provider.go +++ b/pkg/util/qcloud/provider/provider.go @@ -44,6 +44,10 @@ func (self *SQcloudProviderFactory) IsSupportPrepaidResources() bool { return true } +func (self *SQcloudProviderFactory) IsProjectRegional() bool { + return false +} + func (self *SQcloudProviderFactory) NeedSyncSkuFromCloud() bool { return false } diff --git a/pkg/util/qcloud/qcloud.go b/pkg/util/qcloud/qcloud.go index 5695735f55..3fd94c208f 100644 --- a/pkg/util/qcloud/qcloud.go +++ b/pkg/util/qcloud/qcloud.go @@ -78,6 +78,11 @@ func cbsRequest(client *common.Client, apiName string, params map[string]string) return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params) } +func accountRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + domain := "account.api.qcloud.com" + return _phpJsonRequest(client, &wssJsonResponse{}, domain, "/v2/index.php", "", apiName, params) +} + // loadbalancer服务 api 3.0 func clbRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { domain := apiDomain("clb", params) @@ -296,6 +301,14 @@ func (client *SQcloudClient) cbsRequest(apiName string, params map[string]string return cbsRequest(cli, apiName, params) } +func (client *SQcloudClient) accountRequestRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := client.getDefaultClient() + if err != nil { + return nil, err + } + return accountRequest(cli, apiName, params) +} + func (client *SQcloudClient) clbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { cli, err := client.getDefaultClient() if err != nil { diff --git a/pkg/util/qcloud/region.go b/pkg/util/qcloud/region.go index d3464cadb2..be4261c1b9 100644 --- a/pkg/util/qcloud/region.go +++ b/pkg/util/qcloud/region.go @@ -602,6 +602,10 @@ func (self *SRegion) cvmRequest(apiName string, params map[string]string) (jsonu return self.client.jsonRequest(apiName, params) } +func (self *SRegion) accountRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + return self.client.accountRequestRequest(apiName, params) +} + func (self *SRegion) cbsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { params["Region"] = self.Region return self.client.cbsRequest(apiName, params) @@ -773,3 +777,25 @@ func (self *SRegion) GetInstanceStatus(instanceId string) (string, error) { } return instance.InstanceState, nil } + +func (region *SRegion) GetProjects() ([]cloudprovider.ICloudProject, error) { + projects := []SProject{} + params := map[string]string{"allList": "1"} + body, err := region.accountRequest("DescribeProject", params) + if err != nil { + return nil, err + } + if err := body.Unmarshal(&projects); err != nil { + return nil, err + } + projects = append(projects, SProject{ + ProjectId: "0", + ProjectName: "默认项目", + CreateTime: time.Time{}, + }) + iprojects := []cloudprovider.ICloudProject{} + for i := 0; i < len(projects); i++ { + iprojects = append(iprojects, &projects[i]) + } + return iprojects, nil +} diff --git a/pkg/util/qcloud/shell/project.go b/pkg/util/qcloud/shell/project.go new file mode 100644 index 0000000000..be6d93795c --- /dev/null +++ b/pkg/util/qcloud/shell/project.go @@ -0,0 +1,19 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/qcloud" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type ProjectListOptions struct { + } + shellutils.R(&ProjectListOptions{}, "project-list", "List project", func(cli *qcloud.SRegion, args *ProjectListOptions) error { + project, err := cli.GetProjects() + if err != nil { + return err + } + printList(project, 0, 0, 0, nil) + return nil + }) +}