This commit is contained in:
屈轩
2019-03-04 16:36:47 +08:00
parent 032d003fe1
commit 10d4ca66c0
28 changed files with 517 additions and 3 deletions
+44
View File
@@ -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
})
}
+6 -3
View File
@@ -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"
+1
View File
@@ -34,6 +34,7 @@ type ICloudProviderFactory interface {
IsOnPremise() bool
IsSupportPrepaidResources() bool
NeedSyncSkuFromCloud() bool
IsProjectRegional() bool
}
type ICloudProvider interface {
+4
View File
@@ -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
}
+6
View File
@@ -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
}
+200
View File
@@ -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
}
+1
View File
@@ -81,6 +81,7 @@ func InitHandlers(app *appsrv.Application) {
models.DynamicschedtagManager,
models.ServerSkuManager,
models.ExternalProjectManager,
} {
db.RegisterModelManager(manager)
handler := db.NewModelHandler(manager)
@@ -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 {
@@ -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)
}
+12
View File
@@ -0,0 +1,12 @@
package aliyun
import "time"
type SProject struct {
Status string
AccountId string
DisplayName string
Id string
CreateDate time.Time
Name string
}
+4
View File
@@ -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
}
+7
View File
@@ -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
}
+19
View File
@@ -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
})
}
+4
View File
@@ -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
}
+4
View File
@@ -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
}
+4
View File
@@ -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
}
+12
View File
@@ -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
}
+28
View File
@@ -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
}
+4
View File
@@ -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
}
+4
View File
@@ -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
}
+4
View File
@@ -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
}
+4
View File
@@ -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
}
+4
View File
@@ -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
}
+43
View File
@@ -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
}
+4
View File
@@ -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
}
+13
View File
@@ -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 {
+26
View File
@@ -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
}
+19
View File
@@ -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
})
}