From 0d85df52bf651cc6dcf1c5bb973853ec716ac15b Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Wed, 28 Oct 2020 19:15:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96azure=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/multicloud/azure/azure.go | 1035 ++++++----------- pkg/multicloud/azure/classic_disk.go | 4 +- pkg/multicloud/azure/classic_eip.go | 5 +- pkg/multicloud/azure/classic_host.go | 2 +- pkg/multicloud/azure/classic_instance.go | 19 +- pkg/multicloud/azure/classic_network.go | 2 +- pkg/multicloud/azure/classic_secruitygroup.go | 22 +- pkg/multicloud/azure/classic_storage.go | 6 +- pkg/multicloud/azure/classic_vpc.go | 5 +- pkg/multicloud/azure/cloudgroup.go | 14 +- pkg/multicloud/azure/cloudpolicy.go | 32 +- pkg/multicloud/azure/clouduser.go | 55 +- pkg/multicloud/azure/disk.go | 17 +- pkg/multicloud/azure/eip.go | 29 +- pkg/multicloud/azure/enrollment_account.go | 11 +- pkg/multicloud/azure/event.go | 21 +- pkg/multicloud/azure/host.go | 8 +- pkg/multicloud/azure/image.go | 26 +- pkg/multicloud/azure/instance.go | 52 +- pkg/multicloud/azure/instancenic.go | 11 +- pkg/multicloud/azure/monitor.go | 24 +- pkg/multicloud/azure/network.go | 2 +- pkg/multicloud/azure/policy.go | 9 +- pkg/multicloud/azure/region.go | 163 ++- pkg/multicloud/azure/resourcegroup.go | 27 +- pkg/multicloud/azure/resourcesku.go | 53 +- pkg/multicloud/azure/securitygroup.go | 15 +- pkg/multicloud/azure/service.go | 11 +- pkg/multicloud/azure/shell/cloudpolicy.go | 2 +- pkg/multicloud/azure/shell/instance.go | 11 +- pkg/multicloud/azure/shell/resourcegroup.go | 11 +- pkg/multicloud/azure/shell/service.go | 2 +- pkg/multicloud/azure/shell/storageaccount.go | 8 +- pkg/multicloud/azure/shell/subscription.go | 2 +- pkg/multicloud/azure/snapshot.go | 13 +- pkg/multicloud/azure/storage.go | 6 +- pkg/multicloud/azure/storageaccount.go | 62 +- pkg/multicloud/azure/storagecache.go | 10 +- pkg/multicloud/azure/subscription.go | 17 +- pkg/multicloud/azure/usage.go | 3 +- pkg/multicloud/azure/vpc.go | 7 +- pkg/multicloud/azure/wire.go | 2 +- 42 files changed, 746 insertions(+), 1090 deletions(-) diff --git a/pkg/multicloud/azure/azure.go b/pkg/multicloud/azure/azure.go index 687e434191..243f80f2d6 100644 --- a/pkg/multicloud/azure/azure.go +++ b/pkg/multicloud/azure/azure.go @@ -19,7 +19,6 @@ import ( "io/ioutil" "net/http" "net/url" - net_url "net/url" "strconv" "strings" "time" @@ -31,6 +30,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -62,7 +62,7 @@ type SAzureClient struct { env azureenv.Environment authorizer autorest.Authorizer - iregions []cloudprovider.ICloudRegion + regions []SRegion iBuckets []cloudprovider.ICloudBucket subscriptions []SSubscription @@ -70,43 +70,6 @@ type SAzureClient struct { debug bool } -var DEFAULT_API_VERSION = map[string]string{ - "vmSizes": "2018-06-01", //2015-05-01-preview,2015-06-15,2016-03-30,2016-04-30-preview,2016-08-30,2017-03-30,2017-12-01,2018-04-01,2018-06-01,2018-10-01 - "Microsoft.Compute/virtualMachineScaleSets": "2017-12-01", - "Microsoft.Compute/virtualMachines": "2018-04-01", - "Microsoft.ClassicCompute/virtualMachines": "2017-04-01", - "Microsoft.Compute/operations": "2018-10-01", - "Microsoft.ClassicCompute/operations": "2017-04-01", - "Microsoft.Network/virtualNetworks": "2018-08-01", - "Microsoft.ClassicNetwork/virtualNetworks": "2017-11-15", //avaliable 2014-01-01,2014-06-01,2015-06-01,2015-12-01,2016-04-01,2016-11-01,2017-11-15 - "Microsoft.Compute/disks": "2018-06-01", //avaliable 2016-04-30-preview,2017-03-30,2018-04-01,2018-06-01 - "Microsoft.Storage/storageAccounts": "2016-12-01", //2018-03-01-preview,2018-02-01,2017-10-01,2017-06-01,2016-12-01,2016-05-01,2016-01-01,2015-06-15,2015-05-01-preview - "Microsoft.ClassicStorage/storageAccounts": "2016-04-01", //2014-01-01,2014-04-01,2014-04-01-beta,2014-06-01,2015-06-01,2015-12-01,2016-04-01,2016-11-01 - "Microsoft.Compute/snapshots": "2018-06-01", //2016-04-30-preview,2017-03-30,2018-04-01,2018-06-01 - "Microsoft.Compute/images": "2018-10-01", //2016-04-30-preview,2016-08-30,2017-03-30,2017-12-01,2018-04-01,2018-06-01,2018-10-01 - "Microsoft.Storage": "2016-12-01", //2018-03-01-preview,2018-02-01,2017-10-01,2017-06-01,2016-12-01,2016-05-01,2016-01-01,2015-06-15,2015-05-01-preview - "Microsoft.Network/publicIPAddresses": "2018-06-01", //2014-12-01-preview, 2015-05-01-preview, 2015-06-15, 2016-03-30, 2016-06-01, 2016-07-01, 2016-08-01, 2016-09-01, 2016-10-01, 2016-11-01, 2016-12-01, 2017-03-01, 2017-04-01, 2017-06-01, 2017-08-01, 2017-09-01, 2017-10-01, 2017-11-01, 2018-01-01, 2018-02-01, 2018-03-01, 2018-04-01, 2018-05-01, 2018-06-01, 2018-07-01, 2018-08-01 - "Microsoft.Network/networkSecurityGroups": "2018-06-01", - "Microsoft.Network/networkInterfaces": "2018-06-01", //2014-12-01-preview, 2015-05-01-preview, 2015-06-15, 2016-03-30, 2016-06-01, 2016-07-01, 2016-08-01, 2016-09-01, 2016-10-01, 2016-11-01, 2016-12-01, 2017-03-01, 2017-04-01, 2017-06-01, 2017-08-01, 2017-09-01, 2017-10-01, 2017-11-01, 2018-01-01, 2018-02-01, 2018-03-01, 2018-04-01, 2018-05-01, 2018-06-01, 2018-07-01, 2018-08-01 - "Microsoft.Network": "2018-06-01", - "Microsoft.ClassicNetwork/reservedIps": "2016-04-01", //2014-01-01,2014-06-01,2015-06-01,2015-12-01,2016-04-01,2016-11-01 - "Microsoft.ClassicNetwork/networkSecurityGroups": "2016-11-01", //2015-06-01,2015-12-01,2016-04-01,2016-11-01 - "Microsoft.ClassicCompute/domainNames": "2015-12-01", //2014-01-01, 2014-06-01, 2015-06-01, 2015-10-01, 2015-12-01, 2016-04-01, 2016-11-01, 2017-11-01, 2017-11-15 - "Microsoft.Compute/locations": "2018-06-01", - "microsoft.insights/eventtypes/management/values": "2017-03-01-preview", - "Microsoft.Authorization/policyDefinitions": "2019-09-01", - "Microsoft.Authorization/policyAssignments": "2019-09-01", - "Microsoft.Billing": "2018-03-01-preview", - "Microsoft.Authorization": "2018-01-01-preview", -} - -var GRAPH_API_VERSION = map[string]string{ - "Microsoft.DirectoryServices.User": "1.6", - "users": "1.6", - "Microsoft.DirectoryServices.Group": "1.6", - "groups": "1.6", -} - type AzureClientConfig struct { cpcfg cloudprovider.ProviderConfig @@ -150,19 +113,21 @@ func NewAzureClient(cfg *AzureClientConfig) (*SAzureClient, error) { AzureClientConfig: cfg, debug: cfg.debug, } - err := client.fetchSubscriptions() + var err error + client.subscriptions, err = client.ListSubscriptions() if err != nil { - return nil, errors.Wrap(err, "fetchSubscriptions") + return nil, errors.Wrap(err, "ListSubscriptions") } - err = client.fetchRegions() + client.regions, err = client.ListRegions() if err != nil { - return nil, errors.Wrap(err, "fetchRegions") + return nil, errors.Wrapf(err, "ListRegions") } - if len(cfg.subscriptionId) > 0 { - err = client.fetchBuckets() - if err != nil { - return nil, errors.Wrap(err, "fetchBuckets") - } + for i := range client.regions { + client.regions[i].client = &client + } + client.ressourceGroups, err = client.ListResourceGroups() + if err != nil { + return nil, errors.Wrapf(err, "ListResourceGroups") } return &client, nil } @@ -172,7 +137,7 @@ func (self *SAzureClient) getClient(resource TAzureResource) (*autorest.Client, conf := auth.NewClientCredentialsConfig(self.clientId, self.clientSecret, self.tenantId) env, err := azureenv.EnvironmentFromName(self.envName) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "azureenv.EnvironmentFromName(%s)", self.envName) } httpClient := self.cpcfg.HttpClient() @@ -191,7 +156,7 @@ func (self *SAzureClient) getClient(resource TAzureResource) (*autorest.Client, { spt, err := conf.ServicePrincipalToken() if err != nil { - return nil, err + return nil, errors.Wrapf(err, "ServicePrincipalToken") } spt.SetSender(httpClient) client.Authorizer = autorest.NewBearerAuthorizer(spt) @@ -212,76 +177,88 @@ func (self *SAzureClient) getGraphClient() (*autorest.Client, error) { return self.getClient(GraphResource) } -func (self *SAzureClient) jsonRequest(method, url string, body string) (jsonutils.JSONObject, error) { +func (self *SAzureClient) jsonRequest(method, path string, body jsonutils.JSONObject, params url.Values) (jsonutils.JSONObject, error) { cli, err := self.getDefaultClient() if err != nil { - return nil, err + return nil, errors.Wrapf(err, "jsonRequest") } - return jsonRequest(cli, method, self.domain, url, self.subscriptionId, body, DefaultResource) + resp, err := jsonRequest(cli, method, self.domain, path, body, params) + if err != nil { + return nil, errors.Wrapf(err, "jsonRequest") + } + + azErr := func() *AzureError { + for _, key := range []string{"error", "odata.error"} { + if resp.Contains(key) { + e := &AzureError{} + resp.Unmarshal(e) + return e + } + } + return nil + }() + + if azErr != nil { + switch azErr.Code { + case "SubscriptionNotRegistered": + err = self.registerService("Microsoft.Network") + if err != nil { + return nil, errors.Wrapf(err, "self.registerService(Microsoft.Network)") + } + case "MissingSubscriptionRegistration": + for _, serviceType := range azErr.Details { + err = self.registerService(serviceType.Target) + if err != nil { + return nil, errors.Wrapf(err, "self.registerService(%s)", serviceType.Target) + } + } + } + } + return resp, nil } -func (self *SAzureClient) Put(url string, body jsonutils.JSONObject) error { - cli, err := self.getDefaultClient() +func (self *SAzureClient) gjsonRequest(method, path string, body jsonutils.JSONObject, params url.Values) (jsonutils.JSONObject, error) { + cli, err := self.getGraphClient() if err != nil { - return err + return nil, errors.Wrapf(err, "gjsonRequest") } - resp, err := jsonRequest(cli, "PUT", self.domain, url, self.subscriptionId, body.String(), DefaultResource) - if err != nil { - return err + if params == nil { + params = url.Values{} } - if self.debug { - log.Debugf("%s", resp) - } - return nil + params.Set("api-version", "1.6") + return jsonRequest(cli, method, self.domain, path, body, params) } -func (self *SAzureClient) POST(url string, body jsonutils.JSONObject) error { - cli, err := self.getDefaultClient() - if err != nil { - return err - } - resp, err := jsonRequest(cli, "POST", self.domain, url, self.subscriptionId, body.String(), DefaultResource) - if err != nil { - return err - } - if self.debug { - log.Debugf("%s", resp) - } - return nil +func (self *SAzureClient) put(path string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { + params := url.Values{} + params.Set("api-version", self._apiVersion(path, params)) + return self.jsonRequest("PUT", path, body, params) } -func (self *SAzureClient) Patch(url string, body jsonutils.JSONObject) error { - cli, err := self.getDefaultClient() - if err != nil { - return err - } - resp, err := jsonRequest(cli, "PATCH", self.domain, url, self.subscriptionId, body.String(), DefaultResource) - if err != nil { - return err - } - if self.debug { - log.Debugf("%s", resp) - } - return nil +func (self *SAzureClient) post(path string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { + params := url.Values{} + params.Set("api-version", self._apiVersion(path, params)) + return self.jsonRequest("POST", path, body, params) } -func (self *SAzureClient) Get(resourceId string, params []string, retVal interface{}) error { +func (self *SAzureClient) patch(resource string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { + params := url.Values{} + params.Set("api-version", self._apiVersion(resource, params)) + return self.jsonRequest("PATCH", resource, body, params) +} + +func (self *SAzureClient) get(resourceId string, params url.Values, retVal interface{}) error { if len(resourceId) == 0 { return cloudprovider.ErrNotFound } - path := resourceId - if len(params) > 0 { - path += fmt.Sprintf("?%s", strings.Join(params, "&")) + if params == nil { + params = url.Values{} } - cli, err := self.getDefaultClient() + params.Set("api-version", self._apiVersion(resourceId, params)) + body, err := self.jsonRequest("GET", resourceId, nil, params) if err != nil { return err } - body, err := jsonRequest(cli, "GET", self.domain, path, self.subscriptionId, "", DefaultResource) - if err != nil { - return err - } - //fmt.Println(body) err = body.Unmarshal(retVal) if err != nil { return err @@ -289,70 +266,11 @@ func (self *SAzureClient) Get(resourceId string, params []string, retVal interfa return nil } -func (self *SAzureClient) ListVmSizes(location string) (jsonutils.JSONObject, error) { - cli, err := self.getDefaultClient() +func (self *SAzureClient) gcreate(resource string, body jsonutils.JSONObject, retVal interface{}) error { + path := fmt.Sprintf("%s/%s", self.tenantId, resource) + result, err := self.gjsonRequest("POST", path, body, url.Values{}) if err != nil { - return nil, err - } - if len(self.subscriptionId) == 0 { - return nil, fmt.Errorf("need subscription id") - } - url := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/locations/%s/vmSizes", self.subscriptionId, location) - return jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource) -} - -func (self *SAzureClient) ListClassicDisks() (jsonutils.JSONObject, error) { - cli, err := self.getDefaultClient() - if err != nil { - return nil, err - } - if len(self.subscriptionId) == 0 { - return nil, fmt.Errorf("need subscription id") - } - url := fmt.Sprintf("/subscriptions/%s/services/disks", self.subscriptionId) - return jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource) -} - -func (self *SAzureClient) ListAll(resourceType string, retVal interface{}) error { - return self.ListResources(resourceType, retVal, []string{"value"}) -} - -func (self *SAzureClient) ListAllWithNextToken(resourceType string, retVal interface{}) (string, error) { - return self.ListResourcesWithNextLink(resourceType, retVal, []string{"value"}) -} - -func (self *SAzureClient) ListGraphResource(resource string, params url.Values, retVal interface{}) error { - cli, err := self.getGraphClient() - if err != nil { - return err - } - if params == nil { - params = url.Values{} - } - params.Set("api-version", "1.6") - url := fmt.Sprintf("%s/%s?%s", self.tenantId, resource, params.Encode()) - body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", GraphResource) - if err != nil { - return err - } - if retVal != nil { - err = body.Unmarshal(retVal, "value") - if err != nil { - return err - } - } - return nil -} - -func (self *SAzureClient) CreateGraphResource(resource string, body jsonutils.JSONObject, retVal interface{}) error { - cli, err := self.getGraphClient() - if err != nil { - return err - } - url := fmt.Sprintf("%s/%s?api-version=1.6", self.tenantId, resource) - result, err := jsonRequest(cli, "POST", self.domain, url, self.subscriptionId, body.String(), GraphResource) - if err != nil { - return err + return errors.Wrapf(err, "gjsonRequest") } if retVal != nil { return result.Unmarshal(retVal) @@ -360,208 +278,193 @@ func (self *SAzureClient) CreateGraphResource(resource string, body jsonutils.JS return nil } -func (self *SAzureClient) ListResourcesWithNextLink(resourceType string, retVal interface{}, keys []string) (string, error) { - cli, err := self.getDefaultClient() - if err != nil { - return "", err - } - url := "/subscriptions" - if len(self.subscriptionId) > 0 { - url += fmt.Sprintf("/%s", self.subscriptionId) - } - if len(resourceType) > 0 { - url += fmt.Sprintf("/providers/%s", resourceType) - } - body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource) - if err != nil { - return "", err - } - // fmt.Printf("%s: %s\n", resourceType, body) - if retVal != nil { - err = body.Unmarshal(retVal, keys...) - if err != nil { - return "", err - } - } - nextLink, _ := body.GetString("nextLink") - return nextLink, nil +func (self *SAzureClient) gpatch(resource string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { + return self.gjsonRequest("PATCH", resource, body, nil) } -func (self *SAzureClient) ListResources(resourceType string, retVal interface{}, keys []string) error { - _, err := self.ListResourcesWithNextLink(resourceType, retVal, keys) - return err -} - -func (self *SAzureClient) ListResourcesOfMetirc(resourceType string, external_id string, - params map[string]string) (jsonutils.JSONObject, error) { - cli, err := self.getDefaultClient() +func (self *SAzureClient) glist(resource string, params url.Values, retVal interface{}) error { + if params == nil { + params = url.Values{} + } + err := self._glist(resource, params, retVal) if err != nil { - return nil, err - } - //azure的external_id中会包含请求相关的url的处理信息 - url := external_id - if len(resourceType) > 0 { - url += fmt.Sprintf("/providers/%s", resourceType) - } - if len(params) > 0 { - values := net_url.Values{} - for param, value := range params { - values.Add(param, value) - } - url += fmt.Sprintf("?%s", values.Encode()) - } - //body, err := jsonRequest(cli, "GET", "https://management.azure.com", url, self.subscriptionId, "") - body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource) - if err != nil { - return nil, err - } - return body, nil -} - -func (self *SAzureClient) ListSubscriptions() (jsonutils.JSONObject, error) { - cli, err := self.getDefaultClient() - if err != nil { - return nil, err - } - return jsonRequest(cli, "GET", self.domain, "/subscriptions", self.subscriptionId, "", DefaultResource) -} - -func (self *SAzureClient) List(golbalResource string, retVal interface{}) error { - cli, err := self.getDefaultClient() - if err != nil { - return err - } - url := "/subscriptions" - if len(self.subscriptionId) > 0 { - url += fmt.Sprintf("/%s", self.subscriptionId) - } - if len(self.subscriptionId) > 0 && len(golbalResource) > 0 { - url += fmt.Sprintf("/%s", golbalResource) - } - body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource) - if err != nil { - return err - } - return body.Unmarshal(retVal, "value") -} - -func (self *SAzureClient) listSubscriptionResource(subscriptionId, resource string, retVal interface{}) error { - cli, err := self.getDefaultClient() - if err != nil { - return err - } - url := fmt.Sprintf("/subscriptions/%s/%s", subscriptionId, resource) - body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource) - if err != nil { - return err - } - return body.Unmarshal(retVal, "value") -} - -func (self *SAzureClient) ListByTypeWithResourceGroup(resourceGroupName string, Type string, retVal interface{}) error { - cli, err := self.getDefaultClient() - if err != nil { - return err - } - if len(self.subscriptionId) == 0 { - return fmt.Errorf("Missing subscription Info") - } - url := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s", self.subscriptionId, resourceGroupName, Type) - body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource) - if err != nil { - return err - } - return body.Unmarshal(retVal, "value") -} - -func (self *SAzureClient) Delete(resourceId string) error { - cli, err := self.getDefaultClient() - if err != nil { - return err - } - _, err = jsonRequest(cli, "DELETE", self.domain, resourceId, self.subscriptionId, "", DefaultResource) - return err -} - -func (self *SAzureClient) DeleteGraph(resourceId string) error { - cli, err := self.getGraphClient() - if err != nil { - return err - } - _, err = jsonRequest(cli, "DELETE", self.domain, resourceId, self.subscriptionId, "", GraphResource) - return err -} - -func (self *SAzureClient) PerformAction(resourceId string, action string, body string) (jsonutils.JSONObject, error) { - cli, err := self.getDefaultClient() - if err != nil { - return nil, err - } - url := fmt.Sprintf("%s/%s", resourceId, action) - return jsonRequest(cli, "POST", self.domain, url, self.subscriptionId, body, DefaultResource) -} - -func (self *SAzureClient) CreateResourceGroup(name, location string) (*SResourceGroup, error) { - cli, err := self.getDefaultClient() - if err != nil { - return nil, errors.Wrap(err, "getDefaultClient") - } - if len(location) == 0 { - location = self.iregions[0].GetId() - } - subscriptionId, err := self.getDefaultSubscriptionId() - if err != nil { - return nil, errors.Wrap(err, "getDefaultSubscriptionId") - } - //Create resourceGroup - _url := fmt.Sprintf("/subscriptions/%s/resourcegroups/%s", subscriptionId, name) - _, err = jsonRequest(cli, "PUT", self.domain, _url, subscriptionId, fmt.Sprintf(`{"name": "%s", "location": "%s"}`, name, location), DefaultResource) - if err != nil { - return nil, errors.Wrap(err, "jsonRequest") - } - group := &SResourceGroup{} - return group, self.Get(_url, []string{}, group) -} - -func (self *SAzureClient) CreateIProject(name string) (cloudprovider.ICloudProject, error) { - return self.CreateResourceGroup(name, "") -} - -func (self *SAzureClient) getResourceGroups() ([]SResourceGroup, error) { - subscriptionId, err := self.getDefaultSubscriptionId() - if err != nil { - return nil, errors.Wrap(err, "getDefaultSubscriptionId") - } - resourceGroups := []SResourceGroup{} - err = self.listSubscriptionResource(subscriptionId, "resourcegroups", &resourceGroups) - if err != nil { - return nil, errors.Wrap(err, "listSubscriptionResource") - } - return resourceGroups, nil -} - -func (self *SAzureClient) fetchResourceGroup() error { - if len(self.ressourceGroups) > 0 { - return nil - } - var err error - self.ressourceGroups, err = self.getResourceGroups() - if err != nil { - return errors.Wrap(err, "getResourceGroups") + return errors.Wrapf(err, "_glist(%s)", resource) } return nil } -func (self *SAzureClient) checkParams(body jsonutils.JSONObject, params []string) (map[string]string, error) { - result := map[string]string{} - for i := 0; i < len(params); i++ { - data, err := body.GetString(params[i]) - if err != nil { - return nil, fmt.Errorf("Missing %s params", params[i]) - } - result[params[i]] = data +func (self *SAzureClient) _glist(resource string, params url.Values, retVal interface{}) error { + path := fmt.Sprintf("%s/%s", self.tenantId, resource) + body, err := self.gjsonRequest("GET", path, nil, params) + if err != nil { + return err } - return result, nil + err = body.Unmarshal(retVal, "value") + if err != nil { + return errors.Wrapf(err, "body.Unmarshal") + } + return nil +} + +func (self *SAzureClient) list(resource string, params url.Values, retVal interface{}) error { + if params == nil { + params = url.Values{} + } + result := []jsonutils.JSONObject{} + for { + resp, err := self._list(resource, params) + if err != nil { + return errors.Wrapf(err, "_list(%s)", resource) + } + part, err := resp.GetArray("value") + if err != nil { + return errors.Wrapf(err, "resp.GetArray") + } + result = append(result, part...) + nextLink, _ := resp.GetString("nextLink") + if len(nextLink) == 0 { + break + } + link, err := url.Parse(nextLink) + if err != nil { + return errors.Wrapf(err, "url.Parse(%s)", nextLink) + } + key, skiptoken := func() (string, string) { + for _, key := range []string{"$skiptoken", "$skipToken"} { + token := link.Query().Get(key) + if len(token) > 0 { + return key, token + } + } + return "", "" + }() + if len(skiptoken) == 0 { + break + } + params.Set(key, skiptoken) + } + return jsonutils.Update(retVal, result) +} + +func (self *SAzureClient) _apiVersion(resource string, params url.Values) string { + version := params.Get("api-version") + if len(version) > 0 { + return version + } + info := strings.Split(strings.ToLower(resource), "/") + if utils.IsInStringArray("microsoft.compute", info) { + if utils.IsInStringArray("virtualmachines", info) { + return "2018-04-01" + } + if utils.IsInStringArray("skus", info) { + return "2019-04-01" + } + return "2018-06-01" + } else if utils.IsInStringArray("microsoft.classiccompute", info) { + } else if utils.IsInStringArray("microsoft.network", info) { + if utils.IsInStringArray("virtualnetworks", info) { + return "2018-08-01" + } + if utils.IsInStringArray("publicipaddresses", info) { + return "2018-03-01" + } + return "2018-06-01" + } else if utils.IsInStringArray("microsoft.classicnetwork", info) { + return "2016-04-01" + } else if utils.IsInStringArray("microsoft.storage", info) { + if utils.IsInStringArray("storageaccounts", info) { + return "2016-12-01" + } + if utils.IsInStringArray("checknameavailability", info) { + return "2019-04-01" + } + if utils.IsInStringArray("skus", info) { + return "2019-04-01" + } + if utils.IsInStringArray("usages", info) { + return "2018-07-01" + } + } else if utils.IsInStringArray("microsoft.classicstorage", info) { + if utils.IsInStringArray("storageaccounts", info) { + return "2016-04-01" + } + } else if utils.IsInStringArray("microsoft.billing", info) { + return "2018-03-01-preview" + } else if utils.IsInStringArray("microsoft.insights", info) { + return "2017-03-01-preview" + } else if utils.IsInStringArray("microsoft.authorization", info) { + return "2018-01-01-preview" + } + return AZURE_API_VERSION +} + +func (self *SAzureClient) _list(resource string, params url.Values) (jsonutils.JSONObject, error) { + subId := self.subscriptionId + if len(subId) == 0 { + for _, sub := range self.subscriptions { + if sub.State == "Enabled" { + subId = sub.SubscriptionId + } + } + } + path := "/subscriptions" + switch resource { + case "subscriptions": + case "locations", "resourcegroups", "providers": + if len(subId) == 0 { + return nil, fmt.Errorf("no avaiable subscriptions") + } + path = fmt.Sprintf("subscriptions/%s/%s", subId, resource) + default: + if len(subId) == 0 { + return nil, fmt.Errorf("no avaiable subscriptions") + } + path = fmt.Sprintf("subscriptions/%s/providers/%s", self.subscriptionId, resource) + } + params.Set("api-version", self._apiVersion(resource, params)) + return self.jsonRequest("GET", path, nil, params) +} + +func (self *SAzureClient) del(resourceId string) error { + _, err := self.jsonRequest("DELETE", resourceId, nil, url.Values{}) + return err +} + +func (self *SAzureClient) GDelete(resourceId string) error { + return self.gdel(resourceId) +} + +func (self *SAzureClient) gdel(resourceId string) error { + _, err := self.gjsonRequest("DELETE", resourceId, nil, url.Values{}) + if err != nil { + return errors.Wrapf(err, "gdel(%s)", resourceId) + } + return nil +} + +func (self *SAzureClient) perform(resourceId string, action string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { + path := fmt.Sprintf("%s/%s", resourceId, action) + return self.post(path, body) +} + +func (self *SAzureClient) CreateIProject(name string) (cloudprovider.ICloudProject, error) { + if len(self.regions) > 0 { + _, err := self.regions[0].CreateResourceGroup(name) + if err != nil { + return nil, errors.Wrapf(err, "CreateResourceGroup") + } + return self.regions[0].GetResourceGroupDetail(name) + } + return nil, fmt.Errorf("no region found ???") +} + +func (self *SAzureClient) ListResourceGroups() ([]SResourceGroup, error) { + resourceGroups := []SResourceGroup{} + err := self.list("resourcegroups", url.Values{}, &resourceGroups) + if err != nil { + return nil, errors.Wrap(err, "list") + } + return resourceGroups, nil } type AzureErrorDetail struct { @@ -580,209 +483,92 @@ func (e *AzureError) Error() string { return jsonutils.Marshal(e).String() } -func (self *SAzureClient) getUniqName(cli *autorest.Client, resourceGroup string, resourceType, name string, body jsonutils.JSONObject) (string, string, error) { - url := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s", self.subscriptionId, resourceGroup, resourceType, name) - if _, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource); err != nil { - if errors.Cause(err) == cloudprovider.ErrNotFound { - return url, body.String(), nil - } - return "", "", err - } +func (self *SAzureClient) getUniqName(resourceGroup, resourceType, name string) (string, error) { + prefix := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/", self.subscriptionId, resourceGroup, resourceType) + newName := name for i := 0; i < 20; i++ { - url = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s-%d", self.subscriptionId, resourceGroup, resourceType, name, i) - if _, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource); err != nil { - if errors.Cause(err) == cloudprovider.ErrNotFound { - data := body.(*jsonutils.JSONDict) - data.Set("name", jsonutils.NewString(fmt.Sprintf("%s-%d", name, i))) - return url, body.String(), nil - } - return "", "", err + err := self.get(prefix+newName, nil, url.Values{}) + if errors.Cause(err) == cloudprovider.ErrNotFound { + return newName, nil } + info := strings.Split(newName, "-") + num, _ := strconv.Atoi(info[len(info)-1]) + if num > 0 { + info[len(info)-1] = fmt.Sprintf("%d", num+1) + } + newName = strings.Join(info, "-") } - return "", "", fmt.Errorf("not find uniq name for %s[%s]", resourceType, name) + return "", fmt.Errorf("not find uniq name for %s[%s]", resourceType, name) } -func (self *SAzureClient) GetOrCreateResourceGroup(resourceGroup string, location string) error { - err := self.fetchResourceGroup() +func (self *SAzureClient) create(resourceGroup, resourceType, name string, body jsonutils.JSONObject, retVal interface{}) error { + resource := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s", self.subscriptionId, resourceGroup, resourceType, name) + params := url.Values{} + params.Set("api-version", self._apiVersion(resourceType, params)) + resp, err := self.jsonRequest("PUT", resource, body, params) if err != nil { - return errors.Wrap(err, "fetchResourceGroup") - } - for _, group := range self.ressourceGroups { - if strings.ToLower(group.Name) == strings.ToLower(resourceGroup) { - return nil - } - } - _, err = self.CreateResourceGroup(resourceGroup, location) - return err -} - -func (self *SAzureClient) CreateWithResourceGroup(resourceGroup string, body jsonutils.JSONObject, retVal interface{}) error { - cli, err := self.getDefaultClient() - if err != nil { - return errors.Wrap(err, "getDefaultClient") - } - if len(self.subscriptionId) == 0 { - return fmt.Errorf("Missing subscription info") - } - params, err := self.checkParams(body, []string{"type", "name", "location"}) - if err != nil { - return fmt.Errorf("Azure create resource failed: %s", err.Error()) - } - - err = self.fetchResourceGroup() - if err != nil { - return errors.Wrap(err, "fetchResourceGroup") - } - - if len(resourceGroup) == 0 { - if len(self.ressourceGroups) == 0 { - err = self.GetOrCreateResourceGroup("Default", params["location"]) - if err != nil { - return errors.Wrap(err, "GetOrCreateResourceGroup(Default)") - } - resourceGroup = "Default" - } else { - resourceGroup = self.ressourceGroups[0].Name - } - } else { - err = self.GetOrCreateResourceGroup(resourceGroup, params["location"]) - if err != nil { - return errors.Wrapf(err, "GetOrCreateResourceGroup(%s)", resourceGroup) - } - } - - url, reqString, err := self.getUniqName(cli, resourceGroup, params["type"], params["name"], body) - if err != nil { - return err - } - - result, err := jsonRequest(cli, "PUT", self.domain, url, self.subscriptionId, reqString, DefaultResource) - if err != nil { - return err + return errors.Wrapf(err, "jsonRequest") } if retVal != nil { - return result.Unmarshal(retVal) - } - return nil - -} - -func (self *SAzureClient) Create(body jsonutils.JSONObject, retVal interface{}) error { - return self.CreateWithResourceGroup("", body, retVal) -} - -func (self *SAzureClient) CheckNameAvailability(Type string, body string) (jsonutils.JSONObject, error) { - cli, err := self.getDefaultClient() - if err != nil { - return nil, err - } - if len(self.subscriptionId) == 0 { - return nil, fmt.Errorf("Missing subscription ID") - } - url := fmt.Sprintf("/subscriptions/%s/providers/%s/checkNameAvailability", self.subscriptionId, Type) - return jsonRequest(cli, "POST", self.domain, url, self.subscriptionId, body, DefaultResource) -} - -func (self *SAzureClient) Update(body jsonutils.JSONObject, retVal interface{}) error { - cli, err := self.getDefaultClient() - if err != nil { - return err - } - url, err := body.GetString("id") - if err != nil { - return errors.Wrap(err, "failed to found id for update operation") - } - result, err := jsonRequest(cli, "PUT", self.domain, url, self.subscriptionId, body.String(), DefaultResource) - if err != nil { - return err - } - if retVal != nil { - return result.Unmarshal(retVal) + return resp.Unmarshal(retVal) } return nil } -func waitRegisterComplete(client *autorest.Client, domain, subscriptionId string, serviceType string) error { - for i := 1; i < 10; i++ { - result, err := _jsonRequest(client, "GET", domain, fmt.Sprintf("/subscriptions/%s/providers", subscriptionId), "", DefaultResource) - if err != nil { - return err - } - value, err := result.GetArray("value") - if err != nil { - return err - } - for _, v := range value { - namespace, _ := v.GetString("namespace") - if namespace == serviceType { - state, _ := v.GetString("registrationState") - if state == "Registered" { - return nil - } - log.Debugf("service %s state %s waite %d second ...", serviceType, state, i*10) - } - } - time.Sleep(time.Second * time.Duration(i*10)) - } - return fmt.Errorf("wait service %s register timeout", serviceType) +func (self *SAzureClient) CheckNameAvailability(resourceType string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { + path := fmt.Sprintf("/subscriptions/%s/providers/%s/checkNameAvailability", self.subscriptionId, resourceType) + return self.post(path, body) } -func registerService(client *autorest.Client, domain, subscriptionId string, serviceType string) error { - registryUrl := fmt.Sprintf("/subscriptions/%s/providers/%s/register", subscriptionId, serviceType) - result, err := _jsonRequest(client, "POST", domain, registryUrl, "", DefaultResource) - if err != nil || result.Contains("error") { - return fmt.Errorf("failed to register %s service", serviceType) +func (self *SAzureClient) update(body jsonutils.JSONObject, retVal interface{}) error { + id, _ := body.GetString("id") + if len(id) == 0 { + return fmt.Errorf("failed to found id for update operation") } - if state, _ := result.GetString("registrationState"); state == "Registered" { - return nil + params := url.Values{} + params.Set("api-version", self._apiVersion(id, params)) + resp, err := self.jsonRequest("PUT", id, body, params) + if err != nil { + return err } - return waitRegisterComplete(client, domain, subscriptionId, serviceType) + if retVal != nil { + return resp.Unmarshal(retVal) + } + return nil } -func recoverFromError(client *autorest.Client, domain, subscriptionId string, azureErr AzureError) bool { - switch azureErr.Code { - case "SubscriptionNotRegistered": - services := []string{"Microsoft.Network"} +func (self *SAzureClient) waitRegisterComplete(serviceType string) error { + return cloudprovider.Wait(time.Second*10, time.Minute*5, func() (bool, error) { + services, err := self.ListServices() + if err != nil { + return false, errors.Wrapf(err, "ListServices") + } for _, service := range services { - if err := registerService(client, domain, subscriptionId, service); err != nil { - log.Errorf("register %s error: %v", service, err) - return false + if service.Namespace == serviceType { + if service.RegistrationState == "Registered" { + return true, nil + } + log.Debugf("service %s status: %s", service.RegistrationState) } } - return true - case "MissingSubscriptionRegistration": - for _, detail := range azureErr.Details { - log.Errorf("The subscription is not registered to use namespace '%s', try register it", detail.Target) - if err := registerService(client, domain, subscriptionId, detail.Target); err != nil { - log.Errorf("register %s error: %v", detail.Target, err) - return false - } - } - return true - default: - return false - } + return false, nil + }) } -func jsonRequest(client *autorest.Client, method, domain, baseUrl string, subscriptionId string, body string, resourceType TAzureResource) (jsonutils.JSONObject, error) { - result, err := _jsonRequest(client, method, domain, baseUrl, body, resourceType) +func (self *SAzureClient) registerService(serviceType string) error { + resource := fmt.Sprintf("/subscriptions/%s/providers/%s/register", self.subscriptionId, serviceType) + _, err := self.post(resource, nil) + if err != nil { + return errors.Wrapf(err, "post(%s)", resource) + } + return self.waitRegisterComplete(serviceType) +} + +func jsonRequest(client *autorest.Client, method, domain, baseUrl string, body jsonutils.JSONObject, params url.Values) (jsonutils.JSONObject, error) { + result, err := _jsonRequest(client, method, domain, baseUrl, body, params) if err != nil { return nil, err } - for _, errKey := range []string{"error", "odata.error"} { - if result.Contains(errKey) { - azureError := AzureError{} - err := result.Unmarshal(&azureError, errKey) - if err != nil { - return nil, fmt.Errorf(result.String()) - } - if recoverFromError(client, domain, subscriptionId, azureError) { - return _jsonRequest(client, method, domain, baseUrl, body, resourceType) - } - log.Errorf("Azure %s request: %s \nbody: %s error: %v", method, baseUrl, body, result.String()) - return nil, fmt.Errorf(result.String()) - } - } return result, nil } @@ -879,29 +665,11 @@ func waitForComplatetion(client *autorest.Client, req *http.Request, resp *http. return nil, nil } -func _jsonRequest(client *autorest.Client, method, domain, baseURL, body string, resourceType TAzureResource) (result jsonutils.JSONObject, err error) { - version := AZURE_API_VERSION - switch resourceType { - case GraphResource: - version = "1.6" - default: - for resourceType, _version := range DEFAULT_API_VERSION { - if strings.Index(strings.ToLower(baseURL), strings.ToLower(resourceType)) > 0 { - version = _version - } - } - } - url := fmt.Sprintf("%s%s?api-version=%s", domain, baseURL, version) - if strings.Index(baseURL, "?") > 0 { - if strings.Contains(baseURL, "api-version") { - url = domain + baseURL - } else { - url = fmt.Sprintf("%s%s&api-version=%s", domain, baseURL, version) - } - } +func _jsonRequest(client *autorest.Client, method, domain, path string, body jsonutils.JSONObject, params url.Values) (result jsonutils.JSONObject, err error) { + url := fmt.Sprintf("%s/%s?%s", strings.TrimSuffix(domain, "/"), strings.TrimPrefix(path, "/"), params.Encode()) req := &http.Request{} - if len(body) != 0 { - req, err = http.NewRequest(method, url, strings.NewReader(body)) + if body != nil { + req, err = http.NewRequest(method, url, strings.NewReader(body.String())) if err != nil { log.Errorf("Azure %s new request: %s body: %s error: %v", method, url, body, err) return nil, err @@ -948,114 +716,14 @@ func _jsonRequest(client *autorest.Client, method, domain, baseURL, body string, return jsonutils.Parse([]byte(_data)) } -/*func (self *SAzureClient) UpdateAccount(envName, tenantId, appId, appKey, subscriptionId string) error { - if self.tenantId != tenantId || self.secret != secret || self.envName != envName { - if clientInfo, accountInfo := strings.Split(secret, "/"), strings.Split(tenantId, "/"); len(clientInfo) >= 2 && len(accountInfo) >= 1 { - self.clientId, self.clientSecret = clientInfo[0], strings.Join(clientInfo[1:], "/") - self.tenantId = accountInfo[0] - if len(accountInfo) == 2 { - self.subscriptionId = accountInfo[1] - } - err := self.fetchRegions() - if err != nil { - return err - } - return nil - } else { - return httperrors.NewUnauthorizedError("clientId、clientSecret or subscriptId input error") - } - } - return nil -}*/ - -func (self *SAzureClient) getDefaultSubscriptionId() (string, error) { - if len(self.subscriptionId) > 0 { - return self.subscriptionId, nil - } - if len(self.subscriptions) == 0 { - return "", errors.Errorf("no subscriptions found for this azure account") - } - return self.subscriptions[0].SubscriptionId, nil -} - -func (self *SAzureClient) getRegions() ([]SRegion, error) { - subscriptionId, err := self.getDefaultSubscriptionId() - if err != nil { - return nil, errors.Wrap(err, "getDefaultSubscriptionId") - } +func (self *SAzureClient) ListRegions() ([]SRegion, error) { regions := []SRegion{} - err = self.listSubscriptionResource(subscriptionId, "locations", ®ions) - if err != nil { - return nil, errors.Wrap(err, "listSubscriptionResource") - } - return regions, nil -} - -func (self *SAzureClient) fetchRegions() error { - regions, err := self.getRegions() - if err != nil { - return errors.Wrap(err, "getRegions") - } - self.iregions = make([]cloudprovider.ICloudRegion, len(regions)) - for i := 0; i < len(regions); i++ { - regions[i].client = self - regions[i].SubscriptionID = self.subscriptionId - self.iregions[i] = ®ions[i] - } - return nil -} - -func (self *SAzureClient) invalidateIBuckets() { - self.iBuckets = nil -} - -func (self *SAzureClient) getIBuckets() ([]cloudprovider.ICloudBucket, error) { - if self.iBuckets == nil { - err := self.fetchBuckets() - if err != nil { - return nil, errors.Wrap(err, "fetchBuckets") - } - } - return self.iBuckets, nil -} - -func (client *SAzureClient) fetchBuckets() error { - accounts := []SStorageAccount{} - err := client.ListAll("Microsoft.Storage/storageAccounts", &accounts) - if err != nil { - return errors.Wrap(err, "client.ListAll") - } - buckets := make([]cloudprovider.ICloudBucket, 0) - for i := range accounts { - log.Debugf("%s %s %#v", jsonutils.Marshal(accounts[i]), accounts[i].Location, accounts[i]) - region, err := client.getIRegionByRegionId(accounts[i].Location) - if err != nil { - log.Errorf("fail to find region '%s'", accounts[i].Location) - continue - } - accounts[i].region = region.(*SRegion) - buckets = append(buckets, &accounts[i]) - } - client.iBuckets = buckets - return nil + err := self.list("locations", url.Values{}, ®ions) + return regions, err } func (self *SAzureClient) GetRegions() []SRegion { - regions := make([]SRegion, len(self.iregions)) - for i := 0; i < len(regions); i += 1 { - region := self.iregions[i].(*SRegion) - regions[i] = *region - } - return regions -} - -func (self *SAzureClient) fetchSubscriptions() error { - var err error - self.subscriptions, err = self.GetSubscriptions() - if err != nil { - return errors.Wrap(err, "GetSubscriptions") - } - return nil + return self.regions } func (self *SAzureClient) GetSubAccounts() (subAccounts []cloudprovider.SSubAccount, err error) { @@ -1083,46 +751,50 @@ func (self *SAzureClient) GetIamLoginUrl() string { } func (self *SAzureClient) GetIRegions() []cloudprovider.ICloudRegion { - return self.iregions + ret := []cloudprovider.ICloudRegion{} + for i := range self.regions { + ret = append(ret, &self.regions[i]) + } + return ret } func (self *SAzureClient) getDefaultRegion() (cloudprovider.ICloudRegion, error) { - if len(self.iregions) > 0 { - return self.iregions[0], nil + if len(self.regions) > 0 { + return &self.regions[0], nil } return nil, cloudprovider.ErrNotFound } func (self *SAzureClient) getIRegionByRegionId(id string) (cloudprovider.ICloudRegion, error) { - for i := 0; i < len(self.iregions); i += 1 { - if self.iregions[i].GetId() == id { - return self.iregions[i], nil + for i := 0; i < len(self.regions); i += 1 { + if self.regions[i].GetId() == id { + return &self.regions[i], nil } } return nil, cloudprovider.ErrNotFound } func (self *SAzureClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) { - for i := 0; i < len(self.iregions); i += 1 { - if self.iregions[i].GetGlobalId() == id { - return self.iregions[i], nil + for i := 0; i < len(self.regions); i += 1 { + if self.regions[i].GetGlobalId() == id { + return &self.regions[i], nil } } return nil, cloudprovider.ErrNotFound } func (self *SAzureClient) GetRegion(regionId string) *SRegion { - for i := 0; i < len(self.iregions); i += 1 { - if self.iregions[i].GetId() == regionId { - return self.iregions[i].(*SRegion) + for i := 0; i < len(self.regions); i += 1 { + if self.regions[i].GetId() == regionId { + return &self.regions[i] } } return nil } func (self *SAzureClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) { - for i := 0; i < len(self.iregions); i += 1 { - ihost, err := self.iregions[i].GetIHostById(id) + for i := 0; i < len(self.regions); i += 1 { + ihost, err := self.regions[i].GetIHostById(id) if err == nil { return ihost, nil } else if err != cloudprovider.ErrNotFound { @@ -1133,8 +805,8 @@ func (self *SAzureClient) GetIHostById(id string) (cloudprovider.ICloudHost, err } func (self *SAzureClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { - for i := 0; i < len(self.iregions); i += 1 { - ihost, err := self.iregions[i].GetIVpcById(id) + for i := 0; i < len(self.regions); i += 1 { + ihost, err := self.regions[i].GetIVpcById(id) if err == nil { return ihost, nil } else if err != cloudprovider.ErrNotFound { @@ -1145,8 +817,8 @@ func (self *SAzureClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error } func (self *SAzureClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { - for i := 0; i < len(self.iregions); i += 1 { - ihost, err := self.iregions[i].GetIStorageById(id) + for i := 0; i < len(self.regions); i += 1 { + ihost, err := self.regions[i].GetIStorageById(id) if err == nil { return ihost, nil } else if err != cloudprovider.ErrNotFound { @@ -1178,13 +850,14 @@ func getResourceGroup(id string) string { } func (self *SAzureClient) GetIProjects() ([]cloudprovider.ICloudProject, error) { - err := self.fetchResourceGroup() + resourceGroups, err := self.ListResourceGroups() if err != nil { - return nil, errors.Wrap(err, "fetchResourceGroup") + return nil, errors.Wrapf(err, "ListResourceGroups") } iprojects := []cloudprovider.ICloudProject{} - for i := 0; i < len(self.ressourceGroups); i++ { - iprojects = append(iprojects, &self.ressourceGroups[i]) + for i := 0; i < len(resourceGroups); i++ { + resourceGroups[i].client = self + iprojects = append(iprojects, &resourceGroups[i]) } return iprojects, nil } diff --git a/pkg/multicloud/azure/classic_disk.go b/pkg/multicloud/azure/classic_disk.go index 154c90812a..2008bbaaac 100644 --- a/pkg/multicloud/azure/classic_disk.go +++ b/pkg/multicloud/azure/classic_disk.go @@ -48,7 +48,7 @@ type SClassicDisk struct { StorageAccount SubResource } -func (self *SRegion) GetStorageAccountsDisksWithSnapshots(storageaccounts ...*SStorageAccount) ([]SClassicDisk, []SClassicSnapshot, error) { +func (self *SRegion) GetStorageAccountsDisksWithSnapshots(storageaccounts ...SStorageAccount) ([]SClassicDisk, []SClassicSnapshot, error) { disks, snapshots := []SClassicDisk{}, []SClassicSnapshot{} for i := 0; i < len(storageaccounts); i++ { _disks, _snapshots, err := self.GetStorageAccountDisksWithSnapshots(storageaccounts[i]) @@ -61,7 +61,7 @@ func (self *SRegion) GetStorageAccountsDisksWithSnapshots(storageaccounts ...*SS return disks, snapshots, nil } -func (self *SRegion) GetStorageAccountDisksWithSnapshots(storageaccount *SStorageAccount) ([]SClassicDisk, []SClassicSnapshot, error) { +func (self *SRegion) GetStorageAccountDisksWithSnapshots(storageaccount SStorageAccount) ([]SClassicDisk, []SClassicSnapshot, error) { disks, snapshots := []SClassicDisk{}, []SClassicSnapshot{} containers, err := storageaccount.GetContainers() if err != nil { diff --git a/pkg/multicloud/azure/classic_eip.go b/pkg/multicloud/azure/classic_eip.go index 56929f655a..3fe25e253a 100644 --- a/pkg/multicloud/azure/classic_eip.go +++ b/pkg/multicloud/azure/classic_eip.go @@ -15,6 +15,7 @@ package azure import ( + "net/url" "strings" "time" @@ -134,7 +135,7 @@ func (self *SClassicEipAddress) IsEmulated() bool { func (region *SRegion) GetClassicEip(eipId string) (*SClassicEipAddress, error) { eip := SClassicEipAddress{region: region} - return &eip, region.client.Get(eipId, []string{}, &eip) + return &eip, region.get(eipId, url.Values{}, &eip) } func (self *SClassicEipAddress) Refresh() error { @@ -147,7 +148,7 @@ func (self *SClassicEipAddress) Refresh() error { func (region *SRegion) GetClassicEips() ([]SClassicEipAddress, error) { eips := []SClassicEipAddress{} - err := region.client.ListAll("Microsoft.ClassicNetwork/reservedIps", &eips) + err := region.client.list("Microsoft.ClassicNetwork/reservedIps", url.Values{}, &eips) if err != nil { return nil, err } diff --git a/pkg/multicloud/azure/classic_host.go b/pkg/multicloud/azure/classic_host.go index 171f1936f6..1e0bd3e117 100644 --- a/pkg/multicloud/azure/classic_host.go +++ b/pkg/multicloud/azure/classic_host.go @@ -43,7 +43,7 @@ func (self *SClassicHost) GetName() string { } func (self *SClassicHost) GetGlobalId() string { - return fmt.Sprintf("%s/%s-classic", self.zone.region.GetGlobalId(), self.zone.region.SubscriptionID) + return fmt.Sprintf("%s/%s-classic", self.zone.region.GetGlobalId(), self.zone.region.client.subscriptionId) } func (self *SClassicHost) IsEmulated() bool { diff --git a/pkg/multicloud/azure/classic_instance.go b/pkg/multicloud/azure/classic_instance.go index 28bdc58b62..eee2d4e43b 100644 --- a/pkg/multicloud/azure/classic_instance.go +++ b/pkg/multicloud/azure/classic_instance.go @@ -17,6 +17,7 @@ package azure import ( "context" "fmt" + "net/url" "strings" "time" @@ -176,7 +177,7 @@ func (self *SClassicInstance) GetInstanceType() string { func (self *SRegion) GetClassicInstances() ([]SClassicInstance, error) { result := []SClassicInstance{} instances := []SClassicInstance{} - err := self.client.ListAll("Microsoft.ClassicCompute/virtualMachines", &instances) + err := self.client.list("Microsoft.ClassicCompute/virtualMachines", url.Values{}, &instances) if err != nil { return nil, err } @@ -190,7 +191,9 @@ func (self *SRegion) GetClassicInstances() ([]SClassicInstance, error) { func (self *SRegion) GetClassicInstance(instanceId string) (*SClassicInstance, error) { instance := SClassicInstance{} - return &instance, self.client.Get(instanceId, []string{"$expand=instanceView"}, &instance) + params := url.Values{} + params.Add("$expand", "instanceView") + return &instance, self.get(instanceId, params, &instance) } type ClassicInstanceDiskProperties struct { @@ -212,7 +215,7 @@ type ClassicInstanceDisk struct { func (self *SClassicInstance) getDisks() ([]SClassicDisk, error) { disks := []SClassicDisk{} - body, err := self.host.zone.region.client.jsonRequest("GET", fmt.Sprintf("%s/disks", self.ID), "") + body, err := self.host.zone.region.client.jsonRequest("GET", fmt.Sprintf("%s/disks", self.ID), nil, url.Values{}) if err != nil { return nil, err } @@ -350,10 +353,10 @@ func (self *SClassicInstance) DeleteVM(ctx context.Context) error { return err } if self.Properties.NetworkProfile.NetworkSecurityGroup != nil { - self.host.zone.region.client.Delete(self.Properties.NetworkProfile.NetworkSecurityGroup.ID) + self.host.zone.region.del(self.Properties.NetworkProfile.NetworkSecurityGroup.ID) } if self.Properties.DomainName != nil { - self.host.zone.region.client.Delete(self.Properties.DomainName.ID) + self.host.zone.region.del(self.Properties.DomainName.ID) } return nil } @@ -457,7 +460,7 @@ func (self *SClassicInstance) StopVM(ctx context.Context, opts *cloudprovider.Se } func (self *SRegion) StopClassicVM(instanceId string, isForce bool) error { - _, err := self.client.PerformAction(instanceId, "shutdown", "") + _, err := self.perform(instanceId, "shutdown", nil) return err } @@ -511,7 +514,7 @@ func (self *SClassicInstance) AssignSecurityGroup(secgroupId string) error { if self.Properties.NetworkProfile.NetworkSecurityGroup.ID == secgroupId { return nil } - self.host.zone.region.client.Delete(fmt.Sprintf("%s/associatedNetworkSecurityGroups/%s", self.ID, self.Properties.NetworkProfile.NetworkSecurityGroup.Name)) + self.host.zone.region.del(fmt.Sprintf("%s/associatedNetworkSecurityGroups/%s", self.ID, self.Properties.NetworkProfile.NetworkSecurityGroup.Name)) } secgroup, err := self.host.zone.region.GetClassicSecurityGroupDetails(secgroupId) @@ -528,7 +531,7 @@ func (self *SClassicInstance) AssignSecurityGroup(secgroupId string) error { }, }, } - return self.host.zone.region.client.Update(jsonutils.Marshal(data), nil) + return self.host.zone.region.update(jsonutils.Marshal(data), nil) } func (self *SClassicInstance) GetBillingType() string { diff --git a/pkg/multicloud/azure/classic_network.go b/pkg/multicloud/azure/classic_network.go index b67e7013c7..efacf66c94 100644 --- a/pkg/multicloud/azure/classic_network.go +++ b/pkg/multicloud/azure/classic_network.go @@ -67,7 +67,7 @@ func (self *SClassicNetwork) Delete() error { } subnets = append(subnets, network) } - return self.wire.vpc.region.client.Update(jsonutils.Marshal(vpc), self.wire.vpc) + return self.wire.vpc.region.update(jsonutils.Marshal(vpc), self.wire.vpc) } func (self *SClassicNetwork) GetGateway() string { diff --git a/pkg/multicloud/azure/classic_secruitygroup.go b/pkg/multicloud/azure/classic_secruitygroup.go index 7c0a3fdd97..552e4531e0 100644 --- a/pkg/multicloud/azure/classic_secruitygroup.go +++ b/pkg/multicloud/azure/classic_secruitygroup.go @@ -17,6 +17,7 @@ package azure import ( "fmt" "net" + "net/url" "strconv" "strings" "unicode" @@ -186,12 +187,12 @@ func (region *SRegion) CreateClassicSecurityGroup(name string) (*SClassicSecurit Type: "Microsoft.ClassicNetwork/networkSecurityGroups", Location: region.Name, } - return &secgroup, region.client.Create(jsonutils.Marshal(secgroup), &secgroup) + return &secgroup, region.create("", jsonutils.Marshal(secgroup), &secgroup) } func (region *SRegion) GetClassicSecurityGroups(name string) ([]SClassicSecurityGroup, error) { secgroups := []SClassicSecurityGroup{} - err := region.client.ListAll("Microsoft.ClassicNetwork/networkSecurityGroups", &secgroups) + err := region.client.list("Microsoft.ClassicNetwork/networkSecurityGroups", url.Values{}, &secgroups) if err != nil { return nil, err } @@ -206,11 +207,11 @@ func (region *SRegion) GetClassicSecurityGroups(name string) ([]SClassicSecurity func (region *SRegion) GetClassicSecurityGroupDetails(secgroupId string) (*SClassicSecurityGroup, error) { secgroup := SClassicSecurityGroup{region: region} - return &secgroup, region.client.Get(secgroupId, []string{}, &secgroup) + return &secgroup, region.get(secgroupId, url.Values{}, &secgroup) } func (region *SRegion) deleteClassicSecurityGroup(secgroupId string) error { - return region.client.Delete(secgroupId) + return region.del(secgroupId) } func (self *SClassicSecurityGroup) Delete() error { @@ -289,7 +290,10 @@ func convertClassicSecurityGroupRules(rule cloudprovider.SecurityRule) ([]SClass func (self *SRegion) getClassicSecurityGroupRules(secgroupId string) ([]SClassicSecurityGroupRule, error) { rules := []SClassicSecurityGroupRule{} - result, err := self.client.jsonRequest("GET", fmt.Sprintf("%s/securityRules?api-version=2015-06-01", secgroupId), "") + params := url.Values{} + params.Set("api-version", "2015-06-01") + resource := fmt.Sprintf("%s/securityRules", secgroupId) + result, err := self.client.jsonRequest("GET", resource, nil, params) if err != nil { return nil, err } @@ -297,8 +301,10 @@ func (self *SRegion) getClassicSecurityGroupRules(secgroupId string) ([]SClassic } func (self *SRegion) addClassicSecgroupRule(secgroupId string, rule SClassicSecurityGroupRule) error { - url := fmt.Sprintf("%s/securityRules/%s?api-version=2015-06-01", secgroupId, rule.Name) - _, err := self.client.jsonRequest("PUT", url, jsonutils.Marshal(rule).String()) + resource := fmt.Sprintf("%s/securityRules/%s", secgroupId, rule.Name) + params := url.Values{} + params.Set("api-version", "2015-06-01") + _, err := self.client.jsonRequest("PUT", resource, jsonutils.Marshal(rule), params) return err } @@ -308,7 +314,7 @@ func (self *SClassicSecurityGroup) GetProjectId() string { func (self *SClassicSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error { for _, r := range append(inDels, outDels...) { - err := self.region.client.Delete(r.ExternalId) + err := self.region.del(r.ExternalId) if err != nil { return errors.Wrapf(err, "Delete(%s)", r.ExternalId) } diff --git a/pkg/multicloud/azure/classic_storage.go b/pkg/multicloud/azure/classic_storage.go index 1206043b5c..669f3088e0 100644 --- a/pkg/multicloud/azure/classic_storage.go +++ b/pkg/multicloud/azure/classic_storage.go @@ -17,6 +17,7 @@ package azure import ( "strings" + "github.com/pkg/errors" "yunion.io/x/jsonutils" api "yunion.io/x/onecloud/pkg/apis/compute" @@ -100,7 +101,10 @@ func (self *SClassicStorage) GetIDiskById(diskId string) (cloudprovider.ICloudDi func (self *SClassicStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { storageaccount, err := self.zone.region.GetStorageAccountDetail(self.ID) - disks, _, err := self.zone.region.GetStorageAccountDisksWithSnapshots(storageaccount) + if err != nil { + return nil, errors.Wrapf(err, "GetStorageAccountDetail") + } + disks, _, err := self.zone.region.GetStorageAccountDisksWithSnapshots(*storageaccount) if err != nil { return nil, err } diff --git a/pkg/multicloud/azure/classic_vpc.go b/pkg/multicloud/azure/classic_vpc.go index 91ab3a638f..ff6d36e401 100644 --- a/pkg/multicloud/azure/classic_vpc.go +++ b/pkg/multicloud/azure/classic_vpc.go @@ -16,6 +16,7 @@ package azure import ( "fmt" + "net/url" "strings" "yunion.io/x/jsonutils" @@ -91,7 +92,7 @@ func (self *SClassicVpc) GetCidrBlock() string { } func (self *SClassicVpc) Delete() error { - return self.region.client.Delete(self.ID) + return self.region.del(self.ID) } func (self *SClassicVpc) getWire() *SClassicWire { @@ -103,7 +104,7 @@ func (self *SClassicVpc) getWire() *SClassicWire { func (region *SRegion) GetClassicVpc(vpcId string) (*SClassicVpc, error) { vpc := SClassicVpc{region: region} - return &vpc, region.client.Get(vpcId, []string{}, &vpc) + return &vpc, region.get(vpcId, url.Values{}, &vpc) } func (self *SClassicVpc) fetchNetworks() error { diff --git a/pkg/multicloud/azure/cloudgroup.go b/pkg/multicloud/azure/cloudgroup.go index 1892026dac..be595f07ed 100644 --- a/pkg/multicloud/azure/cloudgroup.go +++ b/pkg/multicloud/azure/cloudgroup.go @@ -122,7 +122,7 @@ func (group *SCloudgroup) DetachSystemPolicy(policyId string) error { return errors.Wrapf(err, "GetRule(%s)", assignment.Properties.RoleDefinitionId) } if role.Properties.RoleName == policyId { - return group.client.Delete(assignment.Id) + return group.client.gdel(assignment.Id) } } return nil @@ -142,7 +142,7 @@ func (self *SAzureClient) GetCloudgroups(name string) ([]SCloudgroup, error) { if len(name) > 0 { params.Set("$filter", fmt.Sprintf("displayName eq '%s'", name)) } - err := self.ListGraphResource("groups", params, &groups) + err := self.glist("groups", params, &groups) if err != nil { return nil, err } @@ -180,7 +180,7 @@ func (self *SAzureClient) GetICloudgroupByName(name string) (cloudprovider.IClou func (self *SAzureClient) ListGroupMemebers(id string) ([]SClouduser, error) { users := []SClouduser{} resource := fmt.Sprintf("groups/%s/members", id) - err := self.ListGraphResource(resource, nil, &users) + err := self.glist(resource, nil, &users) if err != nil { return nil, err } @@ -188,7 +188,7 @@ func (self *SAzureClient) ListGroupMemebers(id string) ([]SClouduser, error) { } func (self *SAzureClient) DeleteGroup(id string) error { - return self.DeleteGraph(fmt.Sprintf("%s/groups/%s?api-version=1.6", self.tenantId, id)) + return self.gdel(fmt.Sprintf("%s/groups/%s", self.tenantId, id)) } func (self *SAzureClient) CreateGroup(name, desc string) (*SCloudgroup, error) { @@ -202,7 +202,7 @@ func (self *SAzureClient) CreateGroup(name, desc string) (*SCloudgroup, error) { params["Description"] = desc } group := SCloudgroup{client: self} - err := self.CreateGraphResource("groups", jsonutils.Marshal(params), &group) + err := self.gcreate("groups", jsonutils.Marshal(params), &group) if err != nil { return nil, errors.Wrap(err, "Create") } @@ -220,7 +220,7 @@ func (self *SAzureClient) RemoveGroupUser(id, userName string) error { if len(users) > 1 { return cloudprovider.ErrDuplicateId } - return self.DeleteGraph(fmt.Sprintf("%s/groups/%s/$links/members/%s", self.tenantId, id, users[0].ObjectId)) + return self.gdel(fmt.Sprintf("%s/groups/%s/$links/members/%s", self.tenantId, id, users[0].ObjectId)) } func (self *SAzureClient) CreateICloudgroup(name, desc string) (cloudprovider.ICloudgroup, error) { @@ -247,7 +247,7 @@ func (self *SAzureClient) AddGroupUser(id, userName string) error { params := map[string]string{ "url": fmt.Sprintf("%s%s/directoryObjects/%s", self.domain, self.tenantId, users[0].ObjectId), } - err = self.CreateGraphResource(resource, jsonutils.Marshal(params), nil) + err = self.gcreate(resource, jsonutils.Marshal(params), nil) if err != nil && !strings.Contains(err.Error(), "One or more added object references already exist for the following modified properties") { return err } diff --git a/pkg/multicloud/azure/cloudpolicy.go b/pkg/multicloud/azure/cloudpolicy.go index 25446a2fde..b933453806 100644 --- a/pkg/multicloud/azure/cloudpolicy.go +++ b/pkg/multicloud/azure/cloudpolicy.go @@ -74,11 +74,6 @@ func (role *SCloudpolicy) Delete() error { func (cli *SAzureClient) GetRoles(name, policyType string) ([]SCloudpolicy, error) { ret := []SCloudpolicy{} - subscriptionId, err := cli.getDefaultSubscriptionId() - if err != nil { - return nil, errors.Wrap(err, "getDefaultSubscriptionId") - } - params := url.Values{} filter := []string{} if len(name) > 0 { filter = append(filter, fmt.Sprintf("roleName eq '%s'", name)) @@ -86,16 +81,14 @@ func (cli *SAzureClient) GetRoles(name, policyType string) ([]SCloudpolicy, erro if len(policyType) > 0 { filter = append(filter, fmt.Sprintf("Type eq '%s'", policyType)) } + params := url.Values{} if len(filter) > 0 { params.Set("$filter", strings.Join(filter, " and ")) } - resource := "providers/Microsoft.Authorization/roleDefinitions" - if len(params) > 0 { - resource = fmt.Sprintf("%s?%s", resource, params.Encode()) - } - err = cli.listSubscriptionResource(subscriptionId, resource, &ret) + resource := "Microsoft.Authorization/roleDefinitions" + err := cli.list(resource, params, &ret) if err != nil { - return nil, errors.Wrap(err, "listSubscriptionResource") + return nil, errors.Wrap(err, "list") } return ret, nil } @@ -150,7 +143,7 @@ func (cli *SAzureClient) AssignPolicy(objectId, roleName, subscriptionId string) } for _, subscriptionId := range subscriptionIds { resource := fmt.Sprintf("subscriptions/%s/providers/Microsoft.Authorization/roleAssignments/%s", subscriptionId, stringutils.UUID4()) - err = cli.Put(resource, jsonutils.Marshal(body)) + _, err = cli.put(resource, jsonutils.Marshal(body)) if err != nil { return errors.Wrapf(err, "AssignPolicy %s for subscription %s", roleName, subscriptionId) } @@ -174,28 +167,21 @@ type SAssignment struct { func (cli *SAzureClient) GetAssignments(objectId string) ([]SAssignment, error) { ret := []SAssignment{} - subscriptionId, err := cli.getDefaultSubscriptionId() - if err != nil { - return nil, errors.Wrap(err, "getDefaultSubscriptionId") - } params := url.Values{} if len(objectId) > 0 { params.Set("$filter", fmt.Sprintf("principalId eq '%s'", objectId)) } - resource := "providers/Microsoft.Authorization/roleAssignments" - if len(params) > 0 { - resource = fmt.Sprintf("%s?%s", resource, params.Encode()) - } - err = cli.listSubscriptionResource(subscriptionId, resource, &ret) + resource := "Microsoft.Authorization/roleAssignments" + err := cli.list(resource, params, &ret) if err != nil { - return nil, errors.Wrap(err, "listSubscriptionResource") + return nil, errors.Wrap(err, "list") } return ret, nil } func (cli *SAzureClient) GetRole(roleId string) (*SCloudpolicy, error) { role := &SCloudpolicy{} - err := cli.Get(roleId, nil, role) + err := cli.get(roleId, nil, role) if err != nil { return nil, errors.Wrapf(err, "GetRole(%s)", roleId) } diff --git a/pkg/multicloud/azure/clouduser.go b/pkg/multicloud/azure/clouduser.go index 638f2c125e..7fa60062a8 100644 --- a/pkg/multicloud/azure/clouduser.go +++ b/pkg/multicloud/azure/clouduser.go @@ -119,19 +119,23 @@ func (user *SClouduser) GetICustomCloudpolicies() ([]cloudprovider.ICloudpolicy, } func (user *SClouduser) AttachSystemPolicy(policyId string) error { - subscriptionId, err := user.client.getDefaultSubscriptionId() - if err != nil { - return errors.Wrapf(err, "getDefaultSubscriptionId") + for _, subscription := range user.client.subscriptions { + err := user.client.AssignPolicy(user.ObjectId, policyId, subscription.SubscriptionId) + if err != nil { + return errors.Wrapf(err, "AssignPolicy for subscription %s", subscription.SubscriptionId) + } } - return user.client.AssignPolicy(user.ObjectId, policyId, subscriptionId) + return nil } func (user *SClouduser) AttachCustomPolicy(policyId string) error { - subscriptionId, err := user.client.getDefaultSubscriptionId() - if err != nil { - return errors.Wrapf(err, "getDefaultSubscriptionId") + for _, subscription := range user.client.subscriptions { + err := user.client.AssignPolicy(user.ObjectId, policyId, subscription.SubscriptionId) + if err != nil { + return errors.Wrapf(err, "AssignPolicy for subscription %s", subscription.SubscriptionId) + } } - return user.client.AssignPolicy(user.ObjectId, policyId, subscriptionId) + return nil } func (user *SClouduser) DetachSystemPolicy(policyId string) error { @@ -145,7 +149,7 @@ func (user *SClouduser) DetachSystemPolicy(policyId string) error { return errors.Wrapf(err, "GetRule(%s)", assignment.Properties.RoleDefinitionId) } if role.Properties.RoleName == policyId { - return user.client.Delete(assignment.Id) + return user.client.gdel(assignment.Id) } } return nil @@ -182,30 +186,13 @@ func (user *SClouduser) GetICloudgroups() ([]cloudprovider.ICloudgroup, error) { } func (self *SAzureClient) GetUserGroups(userId string) ([]SCloudgroup, error) { - cli, err := self.getGraphClient() - if err != nil { - return nil, err - } - resource := fmt.Sprintf("%s/users/%s/memberOf", self.tenantId, userId) - resp, err := jsonRequest(cli, "GET", self.domain, resource, self.subscriptionId, "", GraphResource) - if err != nil { - return nil, err - } - groups := []SCloudgroup{} - err = resp.Unmarshal(&groups, "value") - if err != nil { - return nil, errors.Wrap(err, "resp.Unmarshal") - } - return groups, nil + err := self.glist(resource, url.Values{}, groups) + return groups, err } func (self *SAzureClient) ResetClouduserPassword(id, password string) error { - cli, err := self.getGraphClient() - if err != nil { - return err - } body := jsonutils.Marshal(map[string]interface{}{ "passwordPolicies": "DisablePasswordExpiration, DisableStrongPassword", "passwordProfile": map[string]interface{}{ @@ -213,7 +200,7 @@ func (self *SAzureClient) ResetClouduserPassword(id, password string) error { }, }) resource := fmt.Sprintf("%s/users/%s", self.tenantId, id) - _, err = jsonRequest(cli, "PATCH", self.domain, resource, self.subscriptionId, body.String(), GraphResource) + _, err := self.gpatch(resource, body) return err } @@ -223,7 +210,7 @@ func (self *SAzureClient) GetCloudusers(name string) ([]SClouduser, error) { if len(name) > 0 { params.Set("$filter", fmt.Sprintf("userPrincipalName eq '%s'", name)) } - err := self.ListGraphResource("users", params, &users) + err := self.glist("users", params, &users) if err != nil { return nil, err } @@ -231,7 +218,7 @@ func (self *SAzureClient) GetCloudusers(name string) ([]SClouduser, error) { } func (self *SAzureClient) DeleteClouduser(id string) error { - return self.DeleteGraph(fmt.Sprintf("%s/users/%s?api-version=1.6", self.tenantId, id)) + return self.gdel(fmt.Sprintf("%s/users/%s", self.tenantId, id)) } func (self *SAzureClient) GetICloudusers() ([]cloudprovider.IClouduser, error) { @@ -272,9 +259,9 @@ type SDomain struct { func (self *SAzureClient) GetDomains() ([]SDomain, error) { domains := []SDomain{} - err := self.ListGraphResource("domains", nil, &domains) + err := self.glist("domains", nil, &domains) if err != nil { - return nil, errors.Wrap(err, "ListGraphResource") + return nil, errors.Wrap(err, "glist") } return domains, nil } @@ -302,7 +289,7 @@ func (self *SAzureClient) CreateClouduser(name, password string) (*SClouduser, e } params["userPrincipalName"] = fmt.Sprintf("%s@%s", name, domains[0].Name) user := SClouduser{client: self} - err = self.CreateGraphResource("users", jsonutils.Marshal(params), &user) + err = self.gcreate("users", jsonutils.Marshal(params), &user) if err != nil { return nil, errors.Wrap(err, "Create") } diff --git a/pkg/multicloud/azure/disk.go b/pkg/multicloud/azure/disk.go index e0add6c354..a4d038979e 100644 --- a/pkg/multicloud/azure/disk.go +++ b/pkg/multicloud/azure/disk.go @@ -17,6 +17,7 @@ package azure import ( "context" "fmt" + "net/url" "strings" "time" @@ -118,7 +119,7 @@ func (self *SRegion) CreateDisk(storageType string, name string, sizeGb int32, d } disk.Properties.OsType = image.GetOsType() } - return &disk, self.client.CreateWithResourceGroup(resourceGroup, jsonutils.Marshal(disk), &disk) + return &disk, self.create(resourceGroup, jsonutils.Marshal(disk), &disk) } func (self *SRegion) DeleteDisk(diskId string) error { @@ -130,7 +131,7 @@ func (self *SRegion) deleteDisk(diskId string) error { startTime := time.Now() timeout := 5 * time.Minute for { - err := self.client.Delete(diskId) + err := self.del(diskId) if err == nil { return nil } @@ -158,20 +159,20 @@ func (self *SRegion) ResizeDisk(diskId string, sizeGb int32) error { } disk.Properties.DiskSizeGB = sizeGb disk.Properties.ProvisioningState = "" - return self.client.Update(jsonutils.Marshal(disk), nil) + return self.update(jsonutils.Marshal(disk), nil) } return cloudprovider.ErrNotSupported } func (self *SRegion) GetDisk(diskId string) (*SDisk, error) { disk := SDisk{} - return &disk, self.client.Get(diskId, []string{}, &disk) + return &disk, self.get(diskId, url.Values{}, &disk) } func (self *SRegion) GetDisks() ([]SDisk, error) { result := []SDisk{} disks := []SDisk{} - err := self.client.ListAll("Microsoft.Compute/disks", &disks) + err := self.client.list("Microsoft.Compute/disks", url.Values{}, &disks) if err != nil { return nil, err } @@ -346,14 +347,14 @@ func (self *SDisk) GetSnapshotDetail(snapshotId string) (*SSnapshot, error) { func (region *SRegion) GetSnapshotDetail(snapshotId string) (*SSnapshot, error) { snapshot := SSnapshot{region: region} - return &snapshot, region.client.Get(snapshotId, []string{}, &snapshot) + return &snapshot, region.get(snapshotId, url.Values{}, &snapshot) } func (region *SRegion) GetSnapShots(diskId string) ([]SSnapshot, error) { result := []SSnapshot{} if !strings.HasPrefix(diskId, "https://") { snapshots := []SSnapshot{} - err := region.client.ListAll("Microsoft.Compute/snapshots", &snapshots) + err := region.client.list("Microsoft.Compute/snapshots", url.Values{}, &snapshots) if err != nil { return nil, err } @@ -397,7 +398,7 @@ func (self *SRegion) CreateDiskBySnapshot(diskName, snapshotId string) (*SDisk, "type": "Microsoft.Compute/disks", } disk := &SDisk{} - err := self.client.Create(jsonutils.Marshal(params), disk) + err := self.create("", jsonutils.Marshal(params), disk) if err != nil { return nil, errors.Wrapf(err, "CreateDiskBySnapshot.Create") } diff --git a/pkg/multicloud/azure/eip.go b/pkg/multicloud/azure/eip.go index 66d35fe2f0..8d277fc95c 100644 --- a/pkg/multicloud/azure/eip.go +++ b/pkg/multicloud/azure/eip.go @@ -16,9 +16,11 @@ package azure import ( "fmt" + "net/url" "strings" "time" + "github.com/pkg/errors" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -72,7 +74,7 @@ func (region *SRegion) AllocateEIP(eipName, projectId string) (*SEipAddress, err }, Type: "Microsoft.Network/publicIPAddresses", } - err := region.client.CreateWithResourceGroup(projectId, jsonutils.Marshal(eip), &eip) + err := region.create(projectId, jsonutils.Marshal(eip), &eip) if err != nil { return nil, err } @@ -85,7 +87,7 @@ func (region *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudE func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) { eip := SEipAddress{region: region} - return &eip, region.client.Get(eipId, []string{}, &eip) + return &eip, region.get(eipId, url.Values{}, &eip) } func (self *SEipAddress) Associate(conf *cloudprovider.AssociateConfig) error { @@ -102,10 +104,9 @@ func (region *SRegion) AssociateEip(eipId string, instanceId string) error { if err != nil { return err } - log.Errorf("nic: %s", jsonutils.Marshal(nic).PrettyString()) if len(nic.Properties.IPConfigurations) > 0 { nic.Properties.IPConfigurations[0].Properties.PublicIPAddress = &PublicIPAddress{ID: eipId} - return region.client.Update(jsonutils.Marshal(nic), nil) + return region.update(jsonutils.Marshal(nic), nil) } return fmt.Errorf("network interface with no IPConfigurations") } @@ -126,24 +127,18 @@ func (self *SEipAddress) Delete() error { } func (region *SRegion) DeallocateEIP(eipId string) error { - startTime := time.Now() - timeout := time.Minute * 3 - for { - err := region.client.Delete(eipId) + return cloudprovider.Wait(time.Second*5, time.Minute*5, func() (bool, error) { + err := region.del(eipId) if err == nil { - return nil + return true, nil } // {"error":{"code":"PublicIPAddressCannotBeDeleted","details":[],"message":"Public IP address /subscriptions/d4f0ec08-3e28-4ae5-bdf9-3dc7c5b0eeca/resourceGroups/Default/providers/Microsoft.Network/publicIPAddresses/eip-for-test-wwl can not be deleted since it is still allocated to resource /subscriptions/d4f0ec08-3e28-4ae5-bdf9-3dc7c5b0eeca/resourceGroups/Default/providers/Microsoft.Network/networkInterfaces/test-wwl-ipconfig."}} // 刚解绑eip后可能数据未刷新,需要再次尝试 if strings.Contains(err.Error(), "it is still allocated to resource") { - time.Sleep(time.Second * 5) - } else { - return err + return false, nil } - if time.Now().Sub(startTime) > timeout { - return err - } - } + return false, errors.Wrapf(err, "del(%s)", eipId) + }) } func (self *SEipAddress) Dissociate() error { @@ -173,7 +168,7 @@ func (region *SRegion) DissociateEip(eipId string) error { break } } - return region.client.Update(jsonutils.Marshal(nic), nil) + return region.update(jsonutils.Marshal(nic), nil) } func (self *SEipAddress) GetAssociationExternalId() string { diff --git a/pkg/multicloud/azure/enrollment_account.go b/pkg/multicloud/azure/enrollment_account.go index bd07abe7ec..3c435e4953 100644 --- a/pkg/multicloud/azure/enrollment_account.go +++ b/pkg/multicloud/azure/enrollment_account.go @@ -41,7 +41,7 @@ func (cli *SAzureClient) GetEnrollmentAccounts() ([]cloudprovider.SEnrollmentAcc accounts := struct { Value []SEnrollmentAccount }{} - err := cli.Get("providers/Microsoft.Billing/enrollmentAccounts", nil, &accounts) + err := cli.get("providers/Microsoft.Billing/enrollmentAccounts", nil, &accounts) if err != nil { return nil, err } @@ -72,7 +72,8 @@ func (cli *SAzureClient) CreateSubscription(name string, eaId string, offerType "owners": owners, } resource := fmt.Sprintf("providers/Microsoft.Billing/enrollmentAccounts/%s/providers/Microsoft.Subscription/createSubscription", eaId) - return cli.POST(resource, jsonutils.Marshal(body)) + _, err = cli.post(resource, jsonutils.Marshal(body)) + return err } type SServicePrincipal struct { @@ -97,9 +98,5 @@ func (cli *SAzureClient) ListServicePrincipal(appId string) ([]SServicePrincipal params.Set("$filter", fmt.Sprintf(`appId eq '%s'`, cli.clientId)) } result := []SServicePrincipal{} - err := cli.ListGraphResource("servicePrincipals", params, &result) - if err != nil { - return result, errors.Wrap(err, "ListGraphResource.servicePrincipals") - } - return result, nil + return result, cli.glist("servicePrincipals", params, &result) } diff --git a/pkg/multicloud/azure/event.go b/pkg/multicloud/azure/event.go index 4fe5141245..ff9ab09c55 100644 --- a/pkg/multicloud/azure/event.go +++ b/pkg/multicloud/azure/event.go @@ -137,29 +137,18 @@ func (region *SRegion) GetICloudEvents(start time.Time, end time.Time, withReadE func (region *SRegion) GetEvents(start time.Time, end time.Time) ([]SEvent, error) { events := []SEvent{} - params := url.Values{} if start.IsZero() { start = time.Now().AddDate(0, 0, -7) } if end.IsZero() { end = time.Now() } + params := url.Values{} params.Set("$filter", fmt.Sprintf("eventTimestamp ge '%s' and eventTimestamp le '%s' and eventChannels eq 'Admin, Operation' and levels eq 'Critical,Error,Warning,Informational'", start.Format("2006-01-02T15:04:05Z"), end.Format("2006-01-02T15:04:05Z"))) - nextLink := fmt.Sprintf("microsoft.insights/eventtypes/management/values?%s", params.Encode()) - var err error - for { - _events := []SEvent{} - nextLink, err = region.client.ListAllWithNextToken(nextLink, &_events) - if err != nil { - return nil, err - } - events = append(events, _events...) - if len(nextLink) > 0 { - nextLink = nextLink[strings.Index(nextLink, "microsoft.insights"):] - } - if len(nextLink) == 0 || len(_events) == 0 { - break - } + resource := fmt.Sprintf("microsoft.insights/eventtypes/management/values") + err := region.client.list(resource, params, &events) + if err != nil { + return nil, err } return events, nil } diff --git a/pkg/multicloud/azure/host.go b/pkg/multicloud/azure/host.go index e4909ecb54..de15cf7394 100644 --- a/pkg/multicloud/azure/host.go +++ b/pkg/multicloud/azure/host.go @@ -49,7 +49,7 @@ func (self *SHost) GetName() string { } func (self *SHost) GetGlobalId() string { - return fmt.Sprintf("%s/%s", self.zone.region.GetGlobalId(), self.zone.region.SubscriptionID) + return fmt.Sprintf("%s/%s", self.zone.region.GetGlobalId(), self.zone.region.client.subscriptionId) } func (self *SHost) IsEmulated() bool { @@ -74,7 +74,7 @@ func (self *SHost) searchNetorkInterface(IPAddr string, networkId string, secgro if ipConf.Properties.PrivateIPAddress == IPAddr && networkId == ipConf.Properties.Subnet.ID && ipConf.Properties.PrivateIPAllocationMethod == "Static" { if nic.Properties.NetworkSecurityGroup == nil || nic.Properties.NetworkSecurityGroup.ID != secgroupId { nic.Properties.NetworkSecurityGroup = &SSecurityGroup{ID: secgroupId} - if err := self.zone.region.client.Update(jsonutils.Marshal(nic), nil); err != nil { + if err := self.zone.region.update(jsonutils.Marshal(nic), nil); err != nil { log.Errorf("assign secgroup %s for nic %#v failed: %v", secgroupId, nic, err) return nil, err } @@ -217,7 +217,7 @@ func (self *SHost) _createVM(desc *cloudprovider.SManagedVMCreateConfig, nicId s if len(desc.InstanceType) > 0 { instance.Properties.HardwareProfile.VMSize = desc.InstanceType log.Debugf("Try HardwareProfile : %s", desc.InstanceType) - err = self.zone.region.client.CreateWithResourceGroup(desc.ProjectId, jsonutils.Marshal(instance), &instance) + err = self.zone.region.create(desc.ProjectId, jsonutils.Marshal(instance), &instance) if err != nil { log.Errorf("Failed for %s: %s", desc.InstanceType, err) return "", fmt.Errorf("Failed to create specification %s.%s", desc.InstanceType, err.Error()) @@ -228,7 +228,7 @@ func (self *SHost) _createVM(desc *cloudprovider.SManagedVMCreateConfig, nicId s for _, profile := range self.zone.region.getHardwareProfile(desc.Cpu, desc.MemoryMB) { instance.Properties.HardwareProfile.VMSize = profile log.Debugf("Try HardwareProfile : %s", profile) - err = self.zone.region.client.CreateWithResourceGroup(desc.ProjectId, jsonutils.Marshal(instance), &instance) + err = self.zone.region.create(desc.ProjectId, jsonutils.Marshal(instance), &instance) if err != nil { for _, key := range []string{`"code":"InvalidParameter"`, `"code":"NicInUse"`} { if strings.Contains(err.Error(), key) { diff --git a/pkg/multicloud/azure/image.go b/pkg/multicloud/azure/image.go index 63223dd1ad..315fcbbd0f 100644 --- a/pkg/multicloud/azure/image.go +++ b/pkg/multicloud/azure/image.go @@ -17,6 +17,7 @@ package azure import ( "context" "fmt" + "net/url" "strings" "time" @@ -241,7 +242,7 @@ func (self *SRegion) GetImageById(imageId string) (SImage, error) { func (self *SRegion) getPrivateImage(imageId string) (SImage, error) { image := SImage{} - err := self.client.Get(imageId, []string{}, &image) + err := self.get(imageId, url.Values{}, &image) if err != nil { return image, err } @@ -295,7 +296,7 @@ func (self *SRegion) CreateImageByBlob(imageName, osType, blobURI string, diskSi }, Type: "Microsoft.Compute/images", } - return &image, self.client.Create(jsonutils.Marshal(image), &image) + return &image, self.create("", jsonutils.Marshal(image), &image) } func (self *SRegion) CreateImage(snapshotId, imageName, osType, imageDesc string) (*SImage, error) { @@ -315,7 +316,7 @@ func (self *SRegion) CreateImage(snapshotId, imageName, osType, imageDesc string }, Type: "Microsoft.Compute/images", } - return &image, self.client.Create(jsonutils.Marshal(image), &image) + return &image, self.create("", jsonutils.Marshal(image), &image) } func (self *SRegion) getOfferedImages(publishersFilter []string, offersFilter []string, skusFilter []string, verFilter []string, imageType string, latestVer bool) ([]SImage, error) { @@ -388,7 +389,7 @@ func (self *SRegion) GetOfferedImageIDs(publishersFilter []string, offersFilter func (self *SRegion) getPrivateImages() ([]SImage, error) { result := []SImage{} images := []SImage{} - err := self.client.ListAll("Microsoft.Compute/images", &images) + err := self.client.list("Microsoft.Compute/images", url.Values{}, &images) if err != nil { return nil, err } @@ -433,7 +434,7 @@ func (self *SRegion) GetImages(imageType string) ([]SImage, error) { } func (self *SRegion) DeleteImage(imageId string) error { - return self.client.Delete(imageId) + return self.del(imageId) } func (self *SImage) GetBlobUri() string { @@ -464,7 +465,8 @@ type SAzureImageResource struct { func (region *SRegion) GetImagePublishers(filter []string) ([]string, error) { publishers := make([]SAzureImageResource, 0) - err := region.client.ListResources(fmt.Sprintf("Microsoft.Compute/locations/%s/publishers", region.Name), &publishers, nil) + // TODO + err := region.client.list(fmt.Sprintf("Microsoft.Compute/locations/%s/publishers", region.Name), url.Values{}, &publishers) if err != nil { return nil, err } @@ -493,7 +495,7 @@ func (region *SRegion) getImageOffers(publisher string, filter []string) ([]stri log.Warningf("failed to get publisher %s driver", publisher) } offers := make([]SAzureImageResource, 0) - err := region.client.ListResources(fmt.Sprintf("Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers", region.Name, publisher), &offers, nil) + err := region.client.list(fmt.Sprintf("Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers", region.Name, publisher), url.Values{}, &offers) if err != nil { return nil, err } @@ -519,7 +521,7 @@ func (region *SRegion) getImageSkus(publisher string, offer string, filter []str } } skus := make([]SAzureImageResource, 0) - err := region.client.ListResources(fmt.Sprintf("Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus", region.Name, publisher, offer), &skus, nil) + err := region.client.list(fmt.Sprintf("Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus", region.Name, publisher, offer), url.Values{}, &skus) if err != nil { return nil, err } @@ -534,10 +536,12 @@ func (region *SRegion) getImageSkus(publisher string, offer string, filter []str func (region *SRegion) getImageVersions(publisher string, offer string, sku string, filter []string, latestVer bool) ([]string, error) { vers := make([]SAzureImageResource, 0) resource := fmt.Sprintf("Microsoft.Compute/locations/%s/publishers/%s/artifacttypes/vmimage/offers/%s/skus/%s/versions", region.Name, publisher, offer, sku) + params := url.Values{} if latestVer { - resource = resource + "?$top=1&$orderby=name%20desc" + params.Set("$top", "1") + params.Set("orderby", "name desc") } - err := region.client.ListResources(resource, &vers, nil) + err := region.client.list(resource, params, &vers) if err != nil { return nil, err } @@ -558,7 +562,7 @@ func (region *SRegion) getImageDetail(publisher string, offer string, sku string "/artifacttypes/vmimage/offers/" + offer + "/skus/" + sku + "/versions/" + version - return image, region.client.Get(id, []string{}, &image) + return image, region.get(id, url.Values{}, &image) } func (region *SRegion) getOfferedImage(offerId string) (SImage, error) { diff --git a/pkg/multicloud/azure/instance.go b/pkg/multicloud/azure/instance.go index fbaa35a67c..3723bde761 100644 --- a/pkg/multicloud/azure/instance.go +++ b/pkg/multicloud/azure/instance.go @@ -17,6 +17,7 @@ package azure import ( "context" "fmt" + "net/url" "strings" "time" @@ -194,31 +195,28 @@ type SInstance struct { func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) { instance := SInstance{} - return &instance, self.client.Get(instanceId, []string{"$expand=instanceView"}, &instance) + params := url.Values{} + params.Set("$expand", "instanceView") + return &instance, self.get(instanceId, params, &instance) } func (self *SRegion) GetInstanceScaleSets() ([]SInstance, error) { instance := []SInstance{} - return instance, self.client.ListAll("Microsoft.Compute/virtualMachineScaleSets", &instance) + return instance, self.client.list("Microsoft.Compute/virtualMachineScaleSets", url.Values{}, &instance) } func (self *SRegion) GetInstances() ([]SInstance, error) { result := []SInstance{} - instances := []SInstance{} - err := self.client.ListAll("Microsoft.Compute/virtualMachines", &instances) + resource := fmt.Sprintf("Microsoft.Compute/locations/%s/virtualMachines", self.Name) + err := self.client.list(resource, url.Values{}, &result) if err != nil { return nil, err } - for i := 0; i < len(instances); i++ { - if instances[i].Location == self.Name { - result = append(result, instances[i]) - } - } return result, nil } func (self *SRegion) doDeleteVM(instanceId string) error { - return self.client.Delete(instanceId) + return self.del(instanceId) } func (self *SInstance) GetSecurityGroupIds() ([]string, error) { @@ -349,7 +347,7 @@ func (self *SInstance) getStorageInfoByUri(uri string) (*SStorage, *SClassicStor return nil, &storage, nil } } - storageaccounts, err = self.host.zone.region.GetStorageAccounts() + storageaccounts, err = self.host.zone.region.ListStorageAccounts() if err != nil { return nil, nil, err } @@ -580,7 +578,7 @@ func (region *SRegion) AttachDisk(instanceId, diskId string) error { instance.Properties.StorageProfile.DataDisks = dataDisks instance.Properties.ProvisioningState = "" instance.Properties.InstanceView = nil - return region.client.Update(jsonutils.Marshal(instance), nil) + return region.update(jsonutils.Marshal(instance), nil) } func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error { @@ -609,7 +607,7 @@ func (region *SRegion) DetachDisk(instanceId, diskId string) error { instance.Properties.StorageProfile.DataDisks = dataDisks instance.Properties.ProvisioningState = "" instance.Properties.InstanceView = nil - return region.client.Update(jsonutils.Marshal(instance), nil) + return region.update(jsonutils.Marshal(instance), nil) } func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error { @@ -623,7 +621,7 @@ func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.S self.Properties.ProvisioningState = "" self.Properties.InstanceView = nil log.Debugf("Try HardwareProfile : %s", vmSize) - err = self.host.zone.region.client.Update(jsonutils.Marshal(self), nil) + err = self.host.zone.region.update(jsonutils.Marshal(self), nil) if err == nil { return cloudprovider.WaitStatus(self, status, 10*time.Second, 300*time.Second) } @@ -640,7 +638,7 @@ func (self *SInstance) ChangeConfig2(ctx context.Context, instanceType string) e self.Properties.ProvisioningState = "" self.Properties.InstanceView = nil log.Debugf("Try HardwareProfile : %s", instanceType) - err := self.host.zone.region.client.Update(jsonutils.Marshal(self), nil) + err := self.host.zone.region.update(jsonutils.Marshal(self), nil) if err != nil { return errors.Wrap(err, "client.Update") } @@ -697,8 +695,8 @@ func (region *SRegion) execOnLinux(instanceId string, command string) error { Settings: map[string]string{"commandToExecute": command}, }, } - url := fmt.Sprintf("%s/extensions/CustomScript", instanceId) - _, err := region.client.jsonRequest("PUT", url, jsonutils.Marshal(extension).String()) + resource := fmt.Sprintf("%s/extensions/CustomScript", instanceId) + _, err := region.client.jsonRequest("PUT", resource, jsonutils.Marshal(extension), url.Values{}) return err } @@ -715,7 +713,7 @@ func (region *SRegion) resetOvsEnv(instanceId string) error { } func (region *SRegion) deleteExtension(instanceId, extensionName string) error { - return region.client.Delete(fmt.Sprintf("%s/extensions/%s", instanceId, extensionName)) + return region.del(fmt.Sprintf("%s/extensions/%s", instanceId, extensionName)) } func (region *SRegion) resetLoginInfo(instanceId string, setting map[string]string) error { extension := SVirtualMachineExtension{ @@ -727,8 +725,8 @@ func (region *SRegion) resetLoginInfo(instanceId string, setting map[string]stri ProtectedSettings: setting, }, } - url := fmt.Sprintf("%s/extensions/enablevmaccess", instanceId) - _, err := region.client.jsonRequest("PUT", url, jsonutils.Marshal(extension).String()) + resource := fmt.Sprintf("%s/extensions/enablevmaccess", instanceId) + _, err := region.client.jsonRequest("PUT", resource, jsonutils.Marshal(extension), url.Values{}) if err != nil { err = region.deleteExtension(instanceId, "enablevmaccess") if err != nil { @@ -738,8 +736,8 @@ func (region *SRegion) resetLoginInfo(instanceId string, setting map[string]stri if err != nil { return err } - url := fmt.Sprintf("%s/extensions/enablevmaccess", instanceId) - _, err = region.client.jsonRequest("PUT", url, jsonutils.Marshal(extension).String()) + resource := fmt.Sprintf("%s/extensions/enablevmaccess", instanceId) + _, err = region.client.jsonRequest("PUT", resource, jsonutils.Marshal(extension), url.Values{}) return err } return nil @@ -850,7 +848,7 @@ func (region *SRegion) ReplaceSystemDisk(instance *SInstance, cpu int, memoryMb instance.Properties.ProvisioningState = "" instance.Properties.InstanceView = nil instance.Properties.VmId = "" - err = region.client.Update(jsonutils.Marshal(instance), nil) + err = region.update(jsonutils.Marshal(instance), nil) if err != nil { // 更新失败,需要删除之前交换过的系统盘 region.DeleteDisk(instance.Properties.StorageProfile.OsDisk.ManagedDisk.ID) @@ -1058,7 +1056,7 @@ func (self *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) { } func (self *SRegion) StartVM(instanceId string) error { - _, err := self.client.PerformAction(instanceId, "start", "") + _, err := self.perform(instanceId, "start", nil) return err } @@ -1066,7 +1064,7 @@ func (self *SInstance) StartVM(ctx context.Context) error { if err := self.host.zone.region.StartVM(self.ID); err != nil { return err } - self.host.zone.region.client.jsonRequest("PATCH", self.ID, jsonutils.Marshal(self).String()) + self.host.zone.region.client.jsonRequest("PATCH", self.ID, jsonutils.Marshal(self), url.Values{}) return cloudprovider.WaitStatus(self, api.VM_RUNNING, 10*time.Second, 300*time.Second) } @@ -1075,12 +1073,12 @@ func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerSto if err != nil { return err } - self.host.zone.region.client.jsonRequest("PATCH", self.ID, jsonutils.Marshal(self).String()) + self.host.zone.region.client.jsonRequest("PATCH", self.ID, jsonutils.Marshal(self), url.Values{}) return cloudprovider.WaitStatus(self, api.VM_READY, 10*time.Second, 300*time.Second) } func (self *SRegion) StopVM(instanceId string, isForce bool) error { - _, err := self.client.PerformAction(instanceId, "deallocate", "") + _, err := self.perform(instanceId, "deallocate", nil) return err } diff --git a/pkg/multicloud/azure/instancenic.go b/pkg/multicloud/azure/instancenic.go index 123a3e516e..073f8a6f55 100644 --- a/pkg/multicloud/azure/instancenic.go +++ b/pkg/multicloud/azure/instancenic.go @@ -15,6 +15,7 @@ package azure import ( + "net/url" "strings" "yunion.io/x/jsonutils" @@ -71,7 +72,7 @@ func (self *SInstanceNic) GetIP() string { } func (region *SRegion) DeleteNetworkInterface(interfaceId string) error { - return region.client.Delete(interfaceId) + return region.del(interfaceId) } func (self *SInstanceNic) Delete() error { @@ -101,7 +102,7 @@ func (self *SInstanceNic) updateSecurityGroup(secgroupId string) error { if len(secgroupId) > 0 { self.Properties.NetworkSecurityGroup = &SSecurityGroup{ID: secgroupId} } - return region.client.Update(jsonutils.Marshal(self), nil) + return region.update(jsonutils.Marshal(self), nil) } func (self *SInstanceNic) revokeSecurityGroup() error { @@ -132,12 +133,12 @@ func (self *SInstanceNic) GetINetwork() cloudprovider.ICloudNetwork { func (self *SRegion) GetNetworkInterfaceDetail(interfaceId string) (*SInstanceNic, error) { instancenic := SInstanceNic{} - return &instancenic, self.client.Get(interfaceId, []string{}, &instancenic) + return &instancenic, self.get(interfaceId, url.Values{}, &instancenic) } func (self *SRegion) GetNetworkInterfaces() ([]SInstanceNic, error) { interfaces := []SInstanceNic{} - err := self.client.ListAll("Microsoft.Network/networkInterfaces", &interfaces) + err := self.client.list("Microsoft.Network/networkInterfaces", url.Values{}, &interfaces) if err != nil { return nil, err } @@ -177,5 +178,5 @@ func (self *SRegion) CreateNetworkInterface(resourceGroup string, nicName string instancenic.Properties.IPConfigurations[0].Properties.PrivateIPAllocationMethod = "Dynamic" } - return &instancenic, self.client.CreateWithResourceGroup(resourceGroup, jsonutils.Marshal(&instancenic), &instancenic) + return &instancenic, self.create(resourceGroup, jsonutils.Marshal(&instancenic), &instancenic) } diff --git a/pkg/multicloud/azure/monitor.go b/pkg/multicloud/azure/monitor.go index bfb533a47f..b9f385ed9e 100644 --- a/pkg/multicloud/azure/monitor.go +++ b/pkg/multicloud/azure/monitor.go @@ -15,6 +15,8 @@ package azure import ( + "fmt" + "net/url" "time" ) @@ -85,22 +87,18 @@ type MetricValue struct { func (self *SRegion) GetMonitorData(name string, ns string, external_id string, since time.Time, until time.Time) (*ResponseMetirc, error) { - params := map[string]string{ - "metricnamespace": ns, - "metricnames": name, - "interval": "PT1M", - "aggregation": "Average", - "api-version": "2018-01-01", - } + params := url.Values{} + params.Set("metricnamespace", ns) + params.Set("metricnames", name) + params.Set("interval", "PT1M") + params.Set("aggregation", "Average") + params.Set("api-version", "2018-01-01") if !since.IsZero() && !until.IsZero() { - params["timespan"] = since.UTC().Format(time.RFC3339) + "/" + until.UTC().Format(time.RFC3339) - } - rtn, err := self.client.ListResourcesOfMetirc("microsoft.insights/metrics", external_id, params) - if err != nil { - return nil, err + params.Set("timespan", since.UTC().Format(time.RFC3339)+"/"+until.UTC().Format(time.RFC3339)) } + resource := fmt.Sprintf("%s/provider/microsoft.insights/metrics") elements := ResponseMetirc{} - err = rtn.Unmarshal(&elements) + err := self.get(resource, params, &elements) if err != nil { return nil, err } diff --git a/pkg/multicloud/azure/network.go b/pkg/multicloud/azure/network.go index 2486d6c932..c933d2b87f 100644 --- a/pkg/multicloud/azure/network.go +++ b/pkg/multicloud/azure/network.go @@ -71,7 +71,7 @@ func (self *SNetwork) Delete() error { } vpc.Properties.Subnets = &subnets vpc.Properties.ProvisioningState = "" - return self.wire.vpc.region.client.Update(jsonutils.Marshal(vpc), nil) + return self.wire.vpc.region.update(jsonutils.Marshal(vpc), nil) } return nil } diff --git a/pkg/multicloud/azure/policy.go b/pkg/multicloud/azure/policy.go index 495072243c..135911a06f 100644 --- a/pkg/multicloud/azure/policy.go +++ b/pkg/multicloud/azure/policy.go @@ -72,7 +72,7 @@ type SPolicyDefinition struct { func (client *SAzureClient) GetPolicyDefinitions() ([]SPolicyDefinition, error) { definitions := []SPolicyDefinition{} - err := client.ListAll("Microsoft.Authorization/policyDefinitions", &definitions) + err := client.list("Microsoft.Authorization/policyDefinitions", url.Values{}, &definitions) if err != nil { return nil, errors.Wrap(err, "Microsoft.Authorization/policyDefinitions.List") } @@ -81,7 +81,7 @@ func (client *SAzureClient) GetPolicyDefinitions() ([]SPolicyDefinition, error) func (client *SAzureClient) GetPolicyDefinition(id string) (*SPolicyDefinition, error) { definition := &SPolicyDefinition{} - err := client.Get(id, []string{}, definition) + err := client.get(id, url.Values{}, definition) if err != nil { return nil, errors.Wrapf(err, "get %s", id) } @@ -129,10 +129,11 @@ func (assignment *SPolicyAssignment) GetParameters() *jsonutils.JSONDict { func (client *SAzureClient) GetPolicyAssignments(defineId string) ([]SPolicyAssignment, error) { assignments := []SPolicyAssignment{} resource := "Microsoft.Authorization/policyAssignments" + params := url.Values{} if len(defineId) > 0 { - resource += ("?$filter=" + url.PathEscape("policyDefinitionId eq ") + fmt.Sprintf("'%s'", defineId)) + params.Set("$filter", fmt.Sprintf(`policyDefinitionId eq '%s'`, defineId)) } - err := client.ListAll(resource, &assignments) + err := client.list(resource, params, &assignments) if err != nil { return nil, errors.Wrap(err, "Microsoft.Authorization/policyAssignments.List") } diff --git a/pkg/multicloud/azure/region.go b/pkg/multicloud/azure/region.go index 6b07e03626..4c39728043 100644 --- a/pkg/multicloud/azure/region.go +++ b/pkg/multicloud/azure/region.go @@ -16,6 +16,7 @@ package azure import ( "fmt" + "net/url" "strconv" "strings" @@ -48,12 +49,11 @@ type SRegion struct { storageCache *SStoragecache - ID string - SubscriptionID string - Name string - DisplayName string - Latitude string - Longitude string + ID string + Name string + DisplayName string + Latitude string + Longitude string } func (self *SRegion) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { @@ -70,50 +70,37 @@ func (self *SRegion) GetClient() *SAzureClient { return self.client } -func (self *SRegion) GetVMSize(location string) (map[string]SVMSize, error) { - if len(location) == 0 { - location = self.Name - } - body, err := self.client.ListVmSizes(location) - if err != nil { - return nil, err - } - vmSizes := []SVMSize{} - err = body.Unmarshal(&vmSizes, "value") - if err != nil { - return nil, err - } - result := map[string]SVMSize{} - for i := 0; i < len(vmSizes); i++ { - result[vmSizes[i].Name] = vmSizes[i] - } - return result, nil +func (self *SRegion) ListVmSizes() ([]SVMSize, error) { + result := []SVMSize{} + resource := fmt.Sprintf("Microsoft.Compute/locations/%s/vmSizes", self.Name) + return result, self.client.list(resource, url.Values{}, &result) } func (self *SRegion) getHardwareProfile(cpu, memMB int) []string { - if vmSizes, err := self.GetVMSize(""); err != nil { + vmSizes, err := self.ListVmSizes() + if err != nil { return []string{} - } else { - profiles := make([]string, 0) - for vmSize, info := range vmSizes { - if info.MemoryInMB == int32(memMB) && info.NumberOfCores == cpu { - profiles = append(profiles, vmSize) - } - } - return profiles } + result := []string{} + for i := range vmSizes { + if vmSizes[i].MemoryInMB == int32(memMB) && vmSizes[i].NumberOfCores == cpu { + result = append(result, vmSizes[i].Name) + } + } + return result } -func (self *SRegion) getVMSize(size string) (*SVMSize, error) { - vmSizes, err := self.GetVMSize("") +func (self *SRegion) getVMSize(name string) (*SVMSize, error) { + vmSizes, err := self.ListVmSizes() if err != nil { - return nil, err + return nil, errors.Wrapf(err, "ListVmSizes") } - vmSize, ok := vmSizes[size] - if !ok { - return nil, cloudprovider.ErrNotFound + for i := range vmSizes { + if vmSizes[i].Name == name { + return &vmSizes[i], nil + } } - return &vmSize, nil + return nil, errors.Wrapf(cloudprovider.ErrNotFound, name) } func (self *SRegion) GetMetadata() *jsonutils.JSONDict { @@ -191,7 +178,7 @@ func (self *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudpro }, Type: "Microsoft.Network/virtualNetworks", } - return &vpc, self.client.Create(jsonutils.Marshal(vpc), &vpc) + return &vpc, self.create("", jsonutils.Marshal(vpc), &vpc) } func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) { @@ -344,7 +331,7 @@ func (self *SRegion) getStoragecache() *SStoragecache { func (self *SRegion) getVpcs() ([]SVpc, error) { result := []SVpc{} vpcs := []SVpc{} - err := self.client.ListAll("Microsoft.Network/virtualNetworks", &vpcs) + err := self.client.list("Microsoft.Network/virtualNetworks", url.Values{}, &vpcs) if err != nil { return nil, err } @@ -360,7 +347,7 @@ func (self *SRegion) getClassicVpcs() ([]SClassicVpc, error) { result := []SClassicVpc{} for _, resourceType := range []string{"Microsoft.ClassicNetwork/virtualNetworks"} { vpcs := []SClassicVpc{} - err := self.client.ListAll(resourceType, &vpcs) + err := self.client.list(resourceType, url.Values{}, &vpcs) if err != nil { return nil, err } @@ -498,7 +485,7 @@ func (self *SRegion) CreateInstanceSimple(name string, imgId, osType string, cpu func (region *SRegion) GetEips() ([]SEipAddress, error) { eips := []SEipAddress{} - err := region.client.ListAll("Microsoft.Network/publicIPAddresses", &eips) + err := region.client.list("Microsoft.Network/publicIPAddresses", url.Values{}, &eips) if err != nil { return nil, err } @@ -611,16 +598,13 @@ func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAc } func (region *SRegion) GetIBuckets() ([]cloudprovider.ICloudBucket, error) { - iBuckets, err := region.client.getIBuckets() + accounts, err := region.ListStorageAccounts() if err != nil { - return nil, errors.Wrap(err, "getIBuckets") + return nil, errors.Wrapf(err, "ListStorageAccounts") } ret := make([]cloudprovider.ICloudBucket, 0) - for i := range iBuckets { - if iBuckets[i].GetLocation() != region.GetId() { - continue - } - ret = append(ret, iBuckets[i]) + for i := range accounts { + ret = append(ret, &accounts[i]) } return ret, nil } @@ -634,20 +618,19 @@ func (region *SRegion) CreateIBucket(name string, storageClassStr string, acl st } func (region *SRegion) DeleteIBucket(name string) error { - accounts, err := region.GetStorageAccounts() + accounts, err := region.listStorageAccounts() if err != nil { - return errors.Wrap(err, "GetStorageAccounts") + return errors.Wrap(err, "ListStorageAccounts") } for i := range accounts { if accounts[i].Name == name { - err = region.client.Delete(accounts[i].ID) + err = region.del(accounts[i].ID) if err != nil { - return errors.Wrap(err, "region.client.Delete") + return errors.Wrapf(err, "region.del") } return nil } } - region.client.invalidateIBuckets() return nil } @@ -666,3 +649,71 @@ func (region *SRegion) GetIBucketByName(name string) (cloudprovider.ICloudBucket func (region *SRegion) GetCapabilities() []string { return region.client.GetCapabilities() } + +func (self *SRegion) get(resource string, params url.Values, retVal interface{}) error { + return self.client.get(resource, params, retVal) +} + +func (self *SRegion) del(resource string) error { + return self.client.del(resource) +} + +func (self *SRegion) checkResourceGroup(resourceGroup string) (string, error) { + if len(resourceGroup) == 0 { + resourceGroup = "Default" + } + for i := range self.client.ressourceGroups { + if strings.ToLower(self.client.ressourceGroups[i].Name) == strings.ToLower(resourceGroup) { + return resourceGroup, nil + } + } + _, err := self.CreateResourceGroup(resourceGroup) + return resourceGroup, err +} + +type sInfo struct { + Location string + Name string + Type string +} + +func (self *SRegion) createInfo(body jsonutils.JSONObject) (sInfo, error) { + info := sInfo{} + err := body.Unmarshal(&info) + if err != nil { + return info, errors.Wrapf(err, "body.Unmarshal") + } + if len(info.Name) == 0 { + return info, fmt.Errorf("Missing name params") + } + if len(info.Type) == 0 { + return info, fmt.Errorf("Missing type params") + } + return info, nil +} + +func (self *SRegion) create(resourceGroup string, body jsonutils.JSONObject, retVal interface{}) error { + info, err := self.createInfo(body) + if err != nil { + return errors.Wrapf(err, "createInfo") + } + resourceGroup, err = self.checkResourceGroup(resourceGroup) + if err != nil { + return errors.Wrapf(err, "checkResourceGroup") + } + info.Name, err = self.client.getUniqName(resourceGroup, info.Type, info.Name) + if err != nil { + return errors.Wrapf(err, "getUniqName") + } + info.Location = self.Name + jsonutils.Update(&body, info) + return self.client.create(resourceGroup, info.Type, info.Name, body, retVal) +} + +func (self *SRegion) update(body jsonutils.JSONObject, retVal interface{}) error { + return self.client.update(body, retVal) +} + +func (self *SRegion) perform(id, action string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) { + return self.client.perform(id, action, body) +} diff --git a/pkg/multicloud/azure/resourcegroup.go b/pkg/multicloud/azure/resourcegroup.go index a8abcd1de6..8de12b8db5 100644 --- a/pkg/multicloud/azure/resourcegroup.go +++ b/pkg/multicloud/azure/resourcegroup.go @@ -16,6 +16,7 @@ package azure import ( "fmt" + "net/url" "strings" "yunion.io/x/jsonutils" @@ -28,6 +29,8 @@ type GroupProperties struct { } type SResourceGroup struct { + client *SAzureClient + ID string Name string Location string @@ -35,33 +38,29 @@ type SResourceGroup struct { ManagedBy string } -func (self *SRegion) GetResourceGroups() ([]SResourceGroup, error) { - resourceGroups := []SResourceGroup{} - return resourceGroups, self.client.List("resourcegroups", &resourceGroups) -} - func (self *SRegion) GetResourceGroupDetail(groupName string) (*SResourceGroup, error) { resourceGroup := SResourceGroup{} - idStr := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.SubscriptionID, groupName) - return &resourceGroup, self.client.Get(idStr, []string{}, &resourceGroup) + idStr := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.client.subscriptionId, groupName) + return &resourceGroup, self.get(idStr, url.Values{}, &resourceGroup) } // not support update, resource group name is immutable??? func (self *SRegion) UpdateResourceGroup(groupName string, newName string) error { resourceGroup := SResourceGroup{Name: newName} - idStr := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.SubscriptionID, groupName) - return self.client.Patch(idStr, jsonutils.Marshal(&resourceGroup)) + resource := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.client.subscriptionId, groupName) + _, err := self.client.patch(resource, jsonutils.Marshal(&resourceGroup)) + return err } -func (self *SRegion) CreateResourceGroup(groupName string) error { +func (self *SRegion) CreateResourceGroup(groupName string) (jsonutils.JSONObject, error) { resourceGroup := SResourceGroup{Location: self.Name} - idStr := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.SubscriptionID, groupName) - return self.client.Put(idStr, jsonutils.Marshal(resourceGroup)) + idStr := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.client.subscriptionId, groupName) + return self.client.put(idStr, jsonutils.Marshal(resourceGroup)) } func (self *SRegion) DeleteResourceGroup(groupName string) error { - idStr := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.SubscriptionID, groupName) - return self.client.Delete(idStr) + idStr := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.client.subscriptionId, groupName) + return self.del(idStr) } func (r *SResourceGroup) GetName() string { diff --git a/pkg/multicloud/azure/resourcesku.go b/pkg/multicloud/azure/resourcesku.go index 18438e4252..b143337e51 100644 --- a/pkg/multicloud/azure/resourcesku.go +++ b/pkg/multicloud/azure/resourcesku.go @@ -14,12 +14,6 @@ package azure -import ( - "fmt" - - "yunion.io/x/pkg/utils" -) - /* { "capabilities":[ @@ -113,48 +107,7 @@ type SResourceSkusResult struct { } func (self *SAzureClient) ListResourceSkus() ([]SResourceSku, error) { - cli, err := self.getDefaultClient() - if err != nil { - return nil, err - } - if len(self.subscriptionId) == 0 { - return nil, fmt.Errorf("need subscription id") - } - url := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Compute/skus?api-version=2017-09-01", self.subscriptionId) - skus := make([]SResourceSku, 0) - for { - body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "", DefaultResource) - if err != nil { - return nil, err - } - result := SResourceSkusResult{} - err = body.Unmarshal(&result) - if err != nil { - return nil, err - } - skus = append(skus, result.Value...) - if len(result.NextLink) > 0 { - url = result.NextLink - } else { - break - } - } - return skus, nil -} - -func (self *SRegion) GetResourceSkus(location string) ([]SResourceSku, error) { - skus, err := self.client.ListResourceSkus() - if err != nil { - return nil, err - } - if len(location) == 0 { - return skus, nil - } - ret := make([]SResourceSku, 0) - for i := 0; i < len(skus); i += 1 { - if utils.IsInStringArray(location, skus[i].Locations) { - ret = append(ret, skus[i]) - } - } - return ret, nil + skus := []SResourceSku{} + resource := "Microsoft.Compute/skus" + return skus, self.list(resource, nil, &skus) } diff --git a/pkg/multicloud/azure/securitygroup.go b/pkg/multicloud/azure/securitygroup.go index ee7ab931ad..a4217a7e2d 100644 --- a/pkg/multicloud/azure/securitygroup.go +++ b/pkg/multicloud/azure/securitygroup.go @@ -17,6 +17,7 @@ package azure import ( "fmt" "net" + "net/url" "strconv" "strings" "unicode" @@ -311,12 +312,12 @@ func (region *SRegion) CreateSecurityGroup(secName string) (*SSecurityGroup, err Type: "Microsoft.Network/networkSecurityGroups", Location: region.Name, } - return &secgroup, region.client.Create(jsonutils.Marshal(secgroup), &secgroup) + return &secgroup, region.create("", jsonutils.Marshal(secgroup), &secgroup) } func (region *SRegion) GetSecurityGroups(name string) ([]SSecurityGroup, error) { secgroups := []SSecurityGroup{} - err := region.client.ListAll("Microsoft.Network/networkSecurityGroups", &secgroups) + err := region.client.list("Microsoft.Network/networkSecurityGroups", url.Values{}, &secgroups) if err != nil { return nil, err } @@ -332,7 +333,7 @@ func (region *SRegion) GetSecurityGroups(name string) ([]SSecurityGroup, error) func (region *SRegion) GetSecurityGroupDetails(secgroupId string) (*SSecurityGroup, error) { secgroup := SSecurityGroup{region: region} - return &secgroup, region.client.Get(secgroupId, []string{}, &secgroup) + return &secgroup, region.get(secgroupId, url.Values{}, &secgroup) } func (self *SSecurityGroup) Refresh() error { @@ -422,7 +423,7 @@ func (region *SRegion) AttachSecurityToInterfaces(secgroupId string, nicIds []st return err } nic.Properties.NetworkSecurityGroup = &SSecurityGroup{ID: secgroupId} - if err := region.client.Update(jsonutils.Marshal(nic), nil); err != nil { + if err := region.update(jsonutils.Marshal(nic), nil); err != nil { return err } } @@ -453,12 +454,12 @@ func (self *SSecurityGroup) Delete() error { return err } nic.Properties.NetworkSecurityGroup = nil - if err := self.region.client.Update(jsonutils.Marshal(nic), nil); err != nil { + if err := self.region.update(jsonutils.Marshal(nic), nil); err != nil { return err } } } - return self.region.client.Delete(self.ID) + return self.region.del(self.ID) } func (self *SSecurityGroup) SetRules(rules []cloudprovider.SecurityRule) error { @@ -479,7 +480,7 @@ func (self *SSecurityGroup) SetRules(rules []cloudprovider.SecurityRule) error { } self.Properties.SecurityRules = securityRules self.Properties.ProvisioningState = "" - return self.region.client.Update(jsonutils.Marshal(self), nil) + return self.region.update(jsonutils.Marshal(self), nil) } func (self *SSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error { diff --git a/pkg/multicloud/azure/service.go b/pkg/multicloud/azure/service.go index 79385b1c0b..04f0e537e2 100644 --- a/pkg/multicloud/azure/service.go +++ b/pkg/multicloud/azure/service.go @@ -16,6 +16,7 @@ package azure import ( "fmt" + "net/url" ) type SServices struct { @@ -36,24 +37,24 @@ type ResourceType struct { ResourceType string `json:"resourceType,omitempty"` } -func (self *SRegion) ListServices() ([]SService, error) { +func (self *SAzureClient) ListServices() ([]SService, error) { services := []SService{} - return services, self.client.List("providers", &services) + return services, self.list("providers", url.Values{}, &services) } func (self *SRegion) SerciceShow(serviceType string) (*SService, error) { service := SService{} - return &service, self.client.Get("providers/"+serviceType, []string{}, &service) + return &service, self.get("providers/"+serviceType, url.Values{}, &service) } func (self *SRegion) serviceOperation(resourceType, operation string) error { - services, err := self.ListServices() + services, err := self.client.ListServices() if err != nil { return err } for _, service := range services { if service.Namespace == resourceType { - _, err := self.client.jsonRequest("POST", fmt.Sprintf("%s/%s", service.ID, operation), "") + _, err := self.client.jsonRequest("POST", fmt.Sprintf("%s/%s", service.ID, operation), nil, url.Values{}) return err } } diff --git a/pkg/multicloud/azure/shell/cloudpolicy.go b/pkg/multicloud/azure/shell/cloudpolicy.go index 54b26084af..c33c706a44 100644 --- a/pkg/multicloud/azure/shell/cloudpolicy.go +++ b/pkg/multicloud/azure/shell/cloudpolicy.go @@ -61,7 +61,7 @@ func init() { } shellutils.R(&AssignmentIdOption{}, "assignment-delete", "Delete role assignment", func(cli *azure.SRegion, args *AssignmentIdOption) error { - return cli.GetClient().Delete(args.ID) + return cli.GetClient().GDelete(args.ID) }) type ObjectPolicyListOptions struct { diff --git a/pkg/multicloud/azure/shell/instance.go b/pkg/multicloud/azure/shell/instance.go index 327520c4dc..020d2a224f 100644 --- a/pkg/multicloud/azure/shell/instance.go +++ b/pkg/multicloud/azure/shell/instance.go @@ -54,19 +54,18 @@ func init() { }) type InstanceSizeListOptions struct { - Location string } shellutils.R(&InstanceSizeListOptions{}, "instance-size-list", "List intances", func(cli *azure.SRegion, args *InstanceSizeListOptions) error { - if vmSize, err := cli.GetVMSize(args.Location); err != nil { + vmSizes, err := cli.ListVmSizes() + if err != nil { return err - } else { - printObject(vmSize) - return nil } + printList(vmSizes, 0, 0, 0, nil) + return nil }) shellutils.R(&InstanceSizeListOptions{}, "resource-sku-list", "List resource sku", func(cli *azure.SRegion, args *InstanceSizeListOptions) error { - skus, err := cli.GetResourceSkus(args.Location) + skus, err := cli.GetClient().ListResourceSkus() if err != nil { return err } diff --git a/pkg/multicloud/azure/shell/resourcegroup.go b/pkg/multicloud/azure/shell/resourcegroup.go index 4efdac3cc8..b1721c9190 100644 --- a/pkg/multicloud/azure/shell/resourcegroup.go +++ b/pkg/multicloud/azure/shell/resourcegroup.go @@ -25,12 +25,12 @@ func init() { Offset int `help:"page offset"` } shellutils.R(&ResourceGroupListOptions{}, "resource-group-list", "List group", func(cli *azure.SRegion, args *ResourceGroupListOptions) error { - if groups, err := cli.GetResourceGroups(); err != nil { + groups, err := cli.GetClient().ListResourceGroups() + if err != nil { return err - } else { - printList(groups, len(groups), args.Offset, args.Limit, []string{}) - return nil } + printList(groups, len(groups), 0, 0, []string{}) + return nil }) type ResourceGroupOptions struct { @@ -47,10 +47,11 @@ func init() { }) shellutils.R(&ResourceGroupOptions{}, "resource-group-create", "Create resource group", func(cli *azure.SRegion, args *ResourceGroupOptions) error { - err := cli.CreateResourceGroup(args.GROUP) + resp, err := cli.CreateResourceGroup(args.GROUP) if err != nil { return err } + printObject(resp) return nil }) diff --git a/pkg/multicloud/azure/shell/service.go b/pkg/multicloud/azure/shell/service.go index c7dc64de6d..78eade84df 100644 --- a/pkg/multicloud/azure/shell/service.go +++ b/pkg/multicloud/azure/shell/service.go @@ -23,7 +23,7 @@ func init() { type ServiceListOptions struct { } shellutils.R(&ServiceListOptions{}, "service-list", "List providers", func(cli *azure.SRegion, args *ServiceListOptions) error { - services, err := cli.ListServices() + services, err := cli.GetClient().ListServices() if err != nil { return err } diff --git a/pkg/multicloud/azure/shell/storageaccount.go b/pkg/multicloud/azure/shell/storageaccount.go index 46871b4436..839707edfc 100644 --- a/pkg/multicloud/azure/shell/storageaccount.go +++ b/pkg/multicloud/azure/shell/storageaccount.go @@ -25,12 +25,12 @@ func init() { type StorageAccountListOptions struct { } shellutils.R(&StorageAccountListOptions{}, "storage-account-list", "List storage account", func(cli *azure.SRegion, args *StorageAccountListOptions) error { - if accounts, err := cli.GetStorageAccounts(); err != nil { + accounts, err := cli.ListStorageAccounts() + if err != nil { return err - } else { - printList(accounts, len(accounts), 0, 0, []string{}) - return nil } + printList(accounts, len(accounts), 0, 0, []string{}) + return nil }) type StorageAccountOptions struct { diff --git a/pkg/multicloud/azure/shell/subscription.go b/pkg/multicloud/azure/shell/subscription.go index 393ebd5753..16d4b6d394 100644 --- a/pkg/multicloud/azure/shell/subscription.go +++ b/pkg/multicloud/azure/shell/subscription.go @@ -23,7 +23,7 @@ func init() { type SubscriptionListOptions struct { } shellutils.R(&SubscriptionListOptions{}, "subscription-list", "List subscriptions", func(cli *azure.SRegion, args *SubscriptionListOptions) error { - subscriptions, err := cli.GetClient().GetSubscriptions() + subscriptions, err := cli.GetClient().ListSubscriptions() if err != nil { return err } diff --git a/pkg/multicloud/azure/snapshot.go b/pkg/multicloud/azure/snapshot.go index dcdd4ecd20..462c48147c 100644 --- a/pkg/multicloud/azure/snapshot.go +++ b/pkg/multicloud/azure/snapshot.go @@ -15,7 +15,6 @@ package azure import ( - "fmt" "strings" "yunion.io/x/jsonutils" @@ -90,7 +89,7 @@ func (self *SRegion) CreateSnapshot(diskId, snapName, desc string) (*SSnapshot, }, Type: "Microsoft.Compute/snapshots", } - return &snapshot, self.client.Create(jsonutils.Marshal(snapshot), &snapshot) + return &snapshot, self.create("", jsonutils.Marshal(snapshot), &snapshot) } func (self *SSnapshot) Delete() error { @@ -102,7 +101,7 @@ func (self *SSnapshot) GetSizeMb() int32 { } func (self *SRegion) DeleteSnapshot(snapshotId string) error { - return self.client.Delete(snapshotId) + return self.del(snapshotId) } type AccessURIOutput struct { @@ -119,7 +118,11 @@ type AccessURI struct { } func (self *SRegion) GrantAccessSnapshot(snapshotId string) (string, error) { - body, err := self.client.PerformAction(snapshotId, "beginGetAccess", fmt.Sprintf(`{"access": "Read", "durationInSeconds": %d}`, 3600*24)) + params := map[string]interface{}{ + "access": "Read", + "durationInSeconds": 3600 * 24, + } + body, err := self.perform(snapshotId, "beginGetAccess", jsonutils.Marshal(params)) if err != nil { return "", err } @@ -149,7 +152,7 @@ func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { return nil, err } classicSnapshots := []SClassicSnapshot{} - storages, err := self.GetStorageAccounts() + storages, err := self.ListStorageAccounts() if err != nil { return nil, err } diff --git a/pkg/multicloud/azure/storage.go b/pkg/multicloud/azure/storage.go index ed24419e62..4fa95df129 100644 --- a/pkg/multicloud/azure/storage.go +++ b/pkg/multicloud/azure/storage.go @@ -18,6 +18,7 @@ import ( "fmt" "strings" + "github.com/pkg/errors" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -117,10 +118,9 @@ func (self *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { log.Debugf("find disk %s for storage %s", disks[i].GetName(), self.GetName()) } } - storageaccounts, err := self.zone.region.GetStorageAccounts() + storageaccounts, err := self.zone.region.ListStorageAccounts() if err != nil { - log.Errorf("List storage account for get idisks error: %v", err) - return nil, err + return nil, errors.Wrapf(err, "ListStorageAccounts") } for i := 0; i < len(storageaccounts); i++ { storageType := strings.ToLower(storageaccounts[i].Sku.Name) diff --git a/pkg/multicloud/azure/storageaccount.go b/pkg/multicloud/azure/storageaccount.go index 891f9a76d3..807c6e3507 100644 --- a/pkg/multicloud/azure/storageaccount.go +++ b/pkg/multicloud/azure/storageaccount.go @@ -21,6 +21,7 @@ import ( "io" "math/rand" "net/http" + "net/url" "path" "strconv" "strings" @@ -101,19 +102,24 @@ type SStorageAccount struct { Properties AccountProperties `json:"properties"` } -func (self *SRegion) GetStorageAccounts() ([]*SStorageAccount, error) { - iBuckets, err := self.client.getIBuckets() +func (self *SRegion) listStorageAccounts() ([]SStorageAccount, error) { + accounts := []SStorageAccount{} + err := self.client.list("Microsoft.Storage/storageAccounts", url.Values{}, &accounts) if err != nil { - return nil, errors.Wrap(err, "getIBuckets") + return nil, errors.Wrapf(err, "list") } - ret := make([]*SStorageAccount, 0) - for i := range iBuckets { - if iBuckets[i].GetLocation() != self.GetId() { - continue + result := []SStorageAccount{} + for i := range accounts { + if strings.ToLower(accounts[i].Location) == strings.ToLower(self.Name) { + accounts[i].region = self + result = append(result, accounts[i]) } - ret = append(ret, iBuckets[i].(*SStorageAccount)) } - return ret, nil + return result, nil +} + +func (self *SRegion) ListStorageAccounts() ([]SStorageAccount, error) { + return self.listStorageAccounts() } func randomString(prefix string, length int) string { @@ -129,8 +135,11 @@ func randomString(prefix string, length int) string { func (self *SRegion) GetUniqStorageAccountName() string { for { uniqString := randomString("storage", 8) - requestBody := fmt.Sprintf(`{"name": "%s", "type": "Microsoft.Storage/storageAccounts"}`, uniqString) - body, err := self.client.CheckNameAvailability("Microsoft.Storage", requestBody) + params := map[string]string{ + "name": uniqString, + "type": "Microsoft.Storage/storageAccounts", + } + body, err := self.client.CheckNameAvailability("Microsoft.Storage", jsonutils.Marshal(params)) if err != nil { continue } @@ -152,12 +161,12 @@ type sStorageAccountCheckNameAvailabilityOutput struct { } func (self *SRegion) checkStorageAccountNameExist(name string) (bool, error) { - url := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Storage/checkNameAvailability?api-version=2019-04-01", self.client.subscriptionId) + resource := fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Storage/checkNameAvailability?api-version=2019-04-01", self.client.subscriptionId) body := jsonutils.Marshal(sStorageAccountCheckNameAvailabilityInput{ Name: name, Type: "Microsoft.Storage/storageAccounts", }) - resp, err := self.client.jsonRequest("POST", url, body.String()) + resp, err := self.client.jsonRequest("POST", resource, body, url.Values{}) if err != nil { return false, errors.Wrap(err, "jsonRequest") } @@ -196,7 +205,7 @@ type SStorageAccountSku struct { func (self *SRegion) GetStorageAccountSkus() ([]SStorageAccountSku, error) { skus := make([]SStorageAccountSku, 0) - err := self.client.List("providers/Microsoft.Storage/skus?api-version=2019-04-01", &skus) + err := self.client.list("Microsoft.Storage/skus", url.Values{}, &skus) if err != nil { return nil, errors.Wrap(err, "List") } @@ -254,11 +263,10 @@ func (self *SRegion) createStorageAccount(name string, skuName string) (*SStorag Type: "Microsoft.Storage/storageAccounts", } - err := self.client.Create(jsonutils.Marshal(storageaccount), &storageaccount) + err := self.create("", jsonutils.Marshal(storageaccount), &storageaccount) if err != nil { return nil, errors.Wrap(err, "Create") } - self.client.invalidateIBuckets() return &storageaccount, nil } @@ -284,21 +292,21 @@ func (self *SRegion) CreateStorageAccount(storageAccount string) (*SStorageAccou Type: "Microsoft.Storage/storageAccounts", Tags: map[string]string{"id": storageAccount}, } - return &stoargeaccount, self.client.Create(jsonutils.Marshal(stoargeaccount), &stoargeaccount) + return &stoargeaccount, self.create("", jsonutils.Marshal(stoargeaccount), &stoargeaccount) } return nil, err } func (self *SRegion) getStorageAccountID(storageAccount string) (*SStorageAccount, error) { - accounts, err := self.GetStorageAccounts() + accounts, err := self.ListStorageAccounts() if err != nil { - return nil, err + return nil, errors.Wrapf(err, "ListStorageAccounts") } for i := 0; i < len(accounts); i++ { for k, v := range accounts[i].Tags { if k == "id" && v == storageAccount { accounts[i].region = self - return accounts[i], nil + return &accounts[i], nil } } } @@ -307,7 +315,7 @@ func (self *SRegion) getStorageAccountID(storageAccount string) (*SStorageAccoun func (self *SRegion) GetStorageAccountDetail(accountId string) (*SStorageAccount, error) { account := SStorageAccount{region: self} - err := self.client.Get(accountId, []string{}, &account) + err := self.get(accountId, url.Values{}, &account) if err != nil { return nil, err } @@ -321,7 +329,7 @@ type AccountKeys struct { } func (self *SRegion) GetStorageAccountKey(accountId string) (string, error) { - body, err := self.client.PerformAction(accountId, "listKeys", "") + body, err := self.perform(accountId, "listKeys", nil) if err != nil { return "", err } @@ -342,20 +350,20 @@ func (self *SRegion) GetStorageAccountKey(accountId string) (string, error) { } func (self *SRegion) DeleteStorageAccount(accountId string) error { - return self.client.Delete(accountId) + return self.del(accountId) } -func (self *SRegion) GetClassicStorageAccounts() ([]*SStorageAccount, error) { - result := make([]*SStorageAccount, 0) +func (self *SRegion) GetClassicStorageAccounts() ([]SStorageAccount, error) { + result := make([]SStorageAccount, 0) accounts := make([]SStorageAccount, 0) - err := self.client.ListAll("Microsoft.ClassicStorage/storageAccounts", &accounts) + err := self.client.list("Microsoft.ClassicStorage/storageAccounts", url.Values{}, &accounts) if err != nil { return nil, err } for i := 0; i < len(accounts); i++ { if accounts[i].Location == self.Name { accounts[i].region = self - result = append(result, &accounts[i]) + result = append(result, accounts[i]) } } return result, nil diff --git a/pkg/multicloud/azure/storagecache.go b/pkg/multicloud/azure/storagecache.go index 83a67b1411..055e9a7409 100644 --- a/pkg/multicloud/azure/storagecache.go +++ b/pkg/multicloud/azure/storagecache.go @@ -133,9 +133,9 @@ func (self *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.To } func (self *SStoragecache) checkStorageAccount() (*SStorageAccount, error) { - storageaccounts, err := self.region.GetStorageAccounts() + storageaccounts, err := self.region.ListStorageAccounts() if err != nil { - return nil, errors.Wrap(err, "GetStorageAccounts") + return nil, errors.Wrap(err, "ListStorageAccounts") } if len(storageaccounts) == 0 { storageaccount, err := self.region.CreateStorageAccount(self.region.Name) @@ -146,16 +146,16 @@ func (self *SStoragecache) checkStorageAccount() (*SStorageAccount, error) { } for i := 0; i < len(storageaccounts); i++ { if id, ok := storageaccounts[i].Tags["id"]; ok && id == self.region.Name { - return storageaccounts[i], nil + return &storageaccounts[i], nil } } - storageaccount := storageaccounts[0] + storageaccount := &storageaccounts[0] if storageaccount.Tags == nil { storageaccount.Tags = map[string]string{} } storageaccount.Tags["id"] = self.region.Name - err = self.region.client.Update(jsonutils.Marshal(storageaccount), nil) + err = self.region.update(jsonutils.Marshal(storageaccount), nil) if err != nil { return nil, errors.Wrapf(err, "Update(%s)", jsonutils.Marshal(storageaccount).String()) } diff --git a/pkg/multicloud/azure/subscription.go b/pkg/multicloud/azure/subscription.go index 3e8bf49a35..b5f8877906 100644 --- a/pkg/multicloud/azure/subscription.go +++ b/pkg/multicloud/azure/subscription.go @@ -1,6 +1,6 @@ package azure -import "yunion.io/x/pkg/errors" +import "net/url" type SSubscription struct { SubscriptionId string `json:"subscriptionId"` @@ -8,15 +8,8 @@ type SSubscription struct { DisplayName string `json:"displayName"` } -func (self *SAzureClient) GetSubscriptions() ([]SSubscription, error) { - resp, err := self.ListSubscriptions() - if err != nil { - return nil, err - } - subscriptions := []SSubscription{} - err = resp.Unmarshal(&subscriptions, "value") - if err != nil { - return nil, errors.Wrap(err, "resp.Unmarshal") - } - return subscriptions, nil +func (self *SAzureClient) ListSubscriptions() ([]SSubscription, error) { + result := []SSubscription{} + err := self.list("subscriptions", url.Values{}, &result) + return result, err } diff --git a/pkg/multicloud/azure/usage.go b/pkg/multicloud/azure/usage.go index e3f88efb50..47de5d8d25 100644 --- a/pkg/multicloud/azure/usage.go +++ b/pkg/multicloud/azure/usage.go @@ -16,6 +16,7 @@ package azure import ( "fmt" + "net/url" "strings" "yunion.io/x/pkg/errors" @@ -59,7 +60,7 @@ func (u *SUsage) GetCurrentQuotaUsedCount() int { func (region *SRegion) GetUsage(resourceType string) ([]SUsage, error) { usage := []SUsage{} resource := fmt.Sprintf("%s/locations/%s/usages", resourceType, region.Name) - err := region.client.ListAll(resource, &usage) + err := region.client.list(resource, url.Values{}, &usage) if err != nil { return nil, errors.Wrapf(err, "ListAll(%s)", resource) } diff --git a/pkg/multicloud/azure/vpc.go b/pkg/multicloud/azure/vpc.go index 63ba80ab14..c16d596a92 100644 --- a/pkg/multicloud/azure/vpc.go +++ b/pkg/multicloud/azure/vpc.go @@ -15,6 +15,7 @@ package azure import ( + "net/url" "strings" "yunion.io/x/jsonutils" @@ -98,7 +99,7 @@ func (self *SVpc) Delete() error { } func (self *SRegion) DeleteVpc(vpcId string) error { - return self.client.Delete(vpcId) + return self.del(vpcId) } func (self *SVpc) getSecurityGroups() ([]SSecurityGroup, error) { @@ -219,7 +220,7 @@ func (self *SVpc) GetStatus() string { func (region *SRegion) GetVpc(vpcId string) (*SVpc, error) { vpc := SVpc{region: region} - return &vpc, region.client.Get(vpcId, []string{}, &vpc) + return &vpc, region.get(vpcId, url.Values{}, &vpc) } func (self *SVpc) Refresh() error { @@ -244,5 +245,5 @@ func (self *SVpc) GetNetworks() []SNetwork { func (self *SRegion) GetNetworkDetail(networkId string) (*Subnet, error) { subnet := Subnet{} - return &subnet, self.client.Get(networkId, []string{}, &subnet) + return &subnet, self.get(networkId, url.Values{}, &subnet) } diff --git a/pkg/multicloud/azure/wire.go b/pkg/multicloud/azure/wire.go index 5394310776..31ab1993f1 100644 --- a/pkg/multicloud/azure/wire.go +++ b/pkg/multicloud/azure/wire.go @@ -88,7 +88,7 @@ func (self *SRegion) createNetwork(vpc *SVpc, subnetName string, cidr string, de *vpc.Properties.Subnets = append(*vpc.Properties.Subnets, subnet) } vpc.Properties.ProvisioningState = "" - err := self.client.Update(jsonutils.Marshal(vpc), vpc) + err := self.update(jsonutils.Marshal(vpc), vpc) if err != nil { return nil, err }