diff --git a/pkg/cloudprovider/instance.go b/pkg/cloudprovider/instance.go index 6532a9c9ed..f43b6619a4 100644 --- a/pkg/cloudprovider/instance.go +++ b/pkg/cloudprovider/instance.go @@ -62,6 +62,8 @@ type SManagedVMCreateConfig struct { Account string Password string UserData string + ProjectId string + ProjectName string BillingCycle *billing.SBillingCycle } diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index 630d4eb2a8..eee3760d25 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -73,6 +73,24 @@ func (self *SManagedVirtualizedGuestDriver) GetJsonDescAtHost(ctx context.Contex config.IpAddr = nics[0].IpAddr } + provider := host.GetCloudprovider() + projects, _ := provider.GetExternalProjects() + if projects != nil { + for _, project := range projects { + if project.ProjectId == guest.ProjectId { + config.ProjectId = project.ExternalId + config.ProjectName = project.Name + break + } + } + } + if len(config.ProjectName) == 0 { + project, _ := db.TenantCacheManager.FetchById(guest.ProjectId) + if project != nil { + config.ProjectName = project.GetName() + } + } + disks := guest.GetDisks() config.DataDisks = []cloudprovider.SDiskInfo{} diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 1eb787561c..042c44101a 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -1619,3 +1619,13 @@ func (provider *SCloudprovider) GetChangeOwnerCandidateDomainIds() []string { } return []string{} } + +func (self *SCloudprovider) GetExternalProjects() ([]SExternalProject, error) { + q := ExternalProjectManager.Query().Equals("manager_id", self.Id) + projects := []SExternalProject{} + err := db.FetchModelObjects(ExternalProjectManager, q, &projects) + if err != nil { + return nil, errors.Wrap(err, "FetchModelObjects") + } + return projects, nil +} diff --git a/pkg/multicloud/azure/azure.go b/pkg/multicloud/azure/azure.go index ca7670e3a3..fa0958e02c 100644 --- a/pkg/multicloud/azure/azure.go +++ b/pkg/multicloud/azure/azure.go @@ -49,10 +49,11 @@ type SAzureClient struct { domain string baseUrl string - ressourceGroups []SResourceGroup - fetchResourceGroups bool - env azureenv.Environment - authorizer autorest.Authorizer + ressourceGroups []SResourceGroup + currentResourceGroup string + fetchResourceGroups bool + env azureenv.Environment + authorizer autorest.Authorizer iregions []cloudprovider.ICloudRegion iBuckets []cloudprovider.ICloudBucket @@ -388,6 +389,21 @@ func (self *SAzureClient) PerformAction(resourceId string, action string, body s return jsonRequest(cli, "POST", self.domain, url, self.subscriptionId, body) } +func (self *SAzureClient) createAndSetResourceGroup(resourceGroup, location string) error { + cli, err := self.getDefaultClient() + if err != nil { + return errors.Wrap(err, "getDefaultClient") + } + //Create resourceGroup + _url := fmt.Sprintf("/subscriptions/%s/resourcegroups/%s", self.subscriptionId, resourceGroup) + _, err = jsonRequest(cli, "PUT", self.domain, _url, self.subscriptionId, fmt.Sprintf(`{"name": "%s", "location": "%s"}`, resourceGroup, location)) + if err != nil { + return err + } + self.currentResourceGroup = resourceGroup + return nil +} + func (self *SAzureClient) fetchResourceGroup(cli *autorest.Client, location string) error { if !self.fetchResourceGroups { err := self.List("resourcegroups", &self.ressourceGroups) @@ -397,20 +413,6 @@ func (self *SAzureClient) fetchResourceGroup(cli *autorest.Client, location stri } self.fetchResourceGroups = true } - if len(self.ressourceGroups) == 0 { - //Create Default resourceGroup - _url := fmt.Sprintf("/subscriptions/%s/resourcegroups/Default", self.subscriptionId) - body, err := jsonRequest(cli, "PUT", self.domain, _url, self.subscriptionId, fmt.Sprintf(`{"name": "Default", "location": "%s"}`, location)) - if err != nil { - return err - } - resourceGroup := SResourceGroup{} - err = body.Unmarshal(&resourceGroup) - if err != nil { - return err - } - self.ressourceGroups = []SResourceGroup{resourceGroup} - } return nil } @@ -443,7 +445,7 @@ func (e *AzureError) Error() string { } func (self *SAzureClient) getUniqName(cli *autorest.Client, resourceType, name string, body jsonutils.JSONObject) (string, string, error) { - url := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s", self.subscriptionId, self.ressourceGroups[0].Name, resourceType, name) + url := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s", self.subscriptionId, self.currentResourceGroup, resourceType, name) if _, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, ""); err != nil { if err == cloudprovider.ErrNotFound { return url, body.String(), nil @@ -451,7 +453,7 @@ func (self *SAzureClient) getUniqName(cli *autorest.Client, resourceType, name s return "", "", err } for i := 0; i < 20; i++ { - url = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s-%d", self.subscriptionId, self.ressourceGroups[0].Name, resourceType, name, i) + url = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s-%d", self.subscriptionId, self.currentResourceGroup, resourceType, name, i) if _, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, ""); err == cloudprovider.ErrNotFound { if err == cloudprovider.ErrNotFound { data := body.(*jsonutils.JSONDict) @@ -464,6 +466,26 @@ func (self *SAzureClient) getUniqName(cli *autorest.Client, resourceType, name s return "", "", fmt.Errorf("not find uniq name for %s[%s]", resourceType, name) } +func (self *SAzureClient) CreateAndSetResourceGroup(resourceGroup, location string) error { + cli, err := self.getDefaultClient() + if err != nil { + return errors.Wrap(err, "getDefaultClient") + } + if !self.fetchResourceGroups { + err := self.fetchResourceGroup(cli, location) + if err != nil { + return errors.Wrap(err, "fetchResourceGroup") + } + } + for _, group := range self.ressourceGroups { + if strings.ToLower(group.Name) == strings.ToLower(resourceGroup) { + self.currentResourceGroup = resourceGroup + return nil + } + } + return self.createAndSetResourceGroup(resourceGroup, location) +} + func (self *SAzureClient) Create(body jsonutils.JSONObject, retVal interface{}) error { cli, err := self.getDefaultClient() if err != nil { @@ -476,12 +498,11 @@ func (self *SAzureClient) Create(body jsonutils.JSONObject, retVal interface{}) if err != nil { return fmt.Errorf("Azure create resource failed: %s", err.Error()) } - err = self.fetchResourceGroup(cli, params["location"]) - if err != nil { - return err - } - if len(self.ressourceGroups) == 0 { - return fmt.Errorf("Create Default resourceGroup error?") + if len(self.currentResourceGroup) == 0 { + err = self.CreateAndSetResourceGroup("Default", params["location"]) + if err != nil { + return errors.Wrap(err, "CreateAndSetResourceGroup") + } } url, reqString, err := self.getUniqName(cli, params["type"], params["name"], body) diff --git a/pkg/multicloud/azure/host.go b/pkg/multicloud/azure/host.go index ad8bf8cef0..8f4f22fd70 100644 --- a/pkg/multicloud/azure/host.go +++ b/pkg/multicloud/azure/host.go @@ -21,6 +21,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/util/osprofile" "yunion.io/x/pkg/utils" @@ -86,6 +87,13 @@ func (self *SHost) searchNetorkInterface(IPAddr string, networkId string, secgro } func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) { + if len(desc.ProjectName) > 0 { + err := self.zone.region.CreateAndSetResourceGroup(desc.ProjectName) + if err != nil { + return nil, errors.Wrapf(err, "CreateAndSetResourceGroup(%s)", desc.ProjectName) + } + } + net := self.zone.getNetworkById(desc.ExternalNetworkId) if net == nil { return nil, fmt.Errorf("invalid network ID %s", desc.ExternalNetworkId) diff --git a/pkg/multicloud/azure/region.go b/pkg/multicloud/azure/region.go index eec9752dbd..29f8a0bd79 100644 --- a/pkg/multicloud/azure/region.go +++ b/pkg/multicloud/azure/region.go @@ -689,3 +689,7 @@ func (region *SRegion) GetIBucketByName(name string) (cloudprovider.ICloudBucket func (region *SRegion) GetCapabilities() []string { return region.client.GetCapabilities() } + +func (region *SRegion) CreateAndSetResourceGroup(resourceGroup string) error { + return region.client.CreateAndSetResourceGroup(resourceGroup, region.Name) +}