From db0f7656aa8735f8c2341a3dae8ccd877eaae149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Sat, 17 Nov 2018 20:29:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E6=9C=AA=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E5=B0=9D=E8=AF=95=E6=B3=A8=E5=86=8C=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/util/azure/azure.go | 143 +++++++++++++++++++++++++------- pkg/util/azure/debug.go | 4 + pkg/util/azure/service.go | 55 ++++++++++++ pkg/util/azure/shell/service.go | 41 +++++++++ pkg/util/azure/shell/vpc.go | 19 +++++ pkg/util/azure/vpc.go | 6 +- 6 files changed, 239 insertions(+), 29 deletions(-) create mode 100644 pkg/util/azure/service.go create mode 100644 pkg/util/azure/shell/service.go diff --git a/pkg/util/azure/azure.go b/pkg/util/azure/azure.go index 791e6d3fa1..85120b53c1 100644 --- a/pkg/util/azure/azure.go +++ b/pkg/util/azure/azure.go @@ -100,8 +100,10 @@ func (self *SAzureClient) getDefaultClient() (*autorest.Client, error) { return nil, err } client.Authorizer = authorizer - // client.RequestInspector = LogRequest() - // client.ResponseInspector = LogResponse() + if DEBUG { + client.RequestInspector = LogRequest() + client.ResponseInspector = LogResponse() + } return &client, nil } @@ -110,7 +112,7 @@ func (self *SAzureClient) jsonRequest(method, url string, body string) (jsonutil if err != nil { return nil, err } - return jsonRequest(cli, method, self.domain, url, body) + return jsonRequest(cli, method, self.domain, url, self.subscriptionId, body) } func (self *SAzureClient) Get(resourceId string, params []string, retVal interface{}) error { @@ -125,7 +127,7 @@ func (self *SAzureClient) Get(resourceId string, params []string, retVal interfa if err != nil { return err } - body, err := jsonRequest(cli, "GET", self.domain, path, "") + body, err := jsonRequest(cli, "GET", self.domain, path, self.subscriptionId, "") if err != nil { return err } @@ -145,7 +147,7 @@ func (self *SAzureClient) ListVmSizes(location string) (jsonutils.JSONObject, er 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, "") + return jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "") } func (self *SAzureClient) ListClassicDisks() (jsonutils.JSONObject, error) { @@ -157,7 +159,7 @@ func (self *SAzureClient) ListClassicDisks() (jsonutils.JSONObject, error) { return nil, fmt.Errorf("need subscription id") } url := fmt.Sprintf("/subscriptions/%s/services/disks", self.subscriptionId) - return jsonRequest(cli, "GET", self.domain, url, "") + return jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "") } func (self *SAzureClient) ListAll(resourceType string, retVal interface{}) error { @@ -172,7 +174,7 @@ func (self *SAzureClient) ListAll(resourceType string, retVal interface{}) error if len(resourceType) > 0 { url += fmt.Sprintf("/providers/%s", resourceType) } - body, err := jsonRequest(cli, "GET", self.domain, url, "") + body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "") if err != nil { return err } @@ -187,7 +189,7 @@ func (self *SAzureClient) ListSubscriptions() (jsonutils.JSONObject, error) { if err != nil { return nil, err } - return jsonRequest(cli, "GET", self.domain, "/subscriptions", "") + return jsonRequest(cli, "GET", self.domain, "/subscriptions", self.subscriptionId, "") } func (self *SAzureClient) List(golbalResource string, retVal interface{}) error { @@ -202,7 +204,7 @@ func (self *SAzureClient) List(golbalResource string, retVal interface{}) error if len(self.subscriptionId) > 0 && len(golbalResource) > 0 { url += fmt.Sprintf("/%s", golbalResource) } - body, err := jsonRequest(cli, "GET", self.domain, url, "") + body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "") if err != nil { return err } @@ -218,7 +220,7 @@ func (self *SAzureClient) ListByTypeWithResourceGroup(resourceGroupName string, 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, "") + body, err := jsonRequest(cli, "GET", self.domain, url, self.subscriptionId, "") if err != nil { return err } @@ -230,7 +232,7 @@ func (self *SAzureClient) Delete(resourceId string) error { if err != nil { return err } - _, err = jsonRequest(cli, "DELETE", self.domain, resourceId, "") + _, err = jsonRequest(cli, "DELETE", self.domain, resourceId, self.subscriptionId, "") return err } @@ -240,7 +242,7 @@ func (self *SAzureClient) PerformAction(resourceId string, action string, body s return nil, err } url := fmt.Sprintf("%s/%s", resourceId, action) - return jsonRequest(cli, "POST", self.domain, url, body) + return jsonRequest(cli, "POST", self.domain, url, self.subscriptionId, body) } func (self *SAzureClient) fetchResourceGroup(cli *autorest.Client, location string) error { @@ -255,7 +257,7 @@ func (self *SAzureClient) fetchResourceGroup(cli *autorest.Client, location stri if len(self.ressourceGroups) == 0 { //Create Default resourceGroup _url := fmt.Sprintf("/subscriptions/%s/resourcegroups/Default", self.subscriptionId) - body, err := jsonRequest(cli, "PUT", self.domain, _url, fmt.Sprintf(`{"name": "Default", "location": "%s"}`, location)) + body, err := jsonRequest(cli, "PUT", self.domain, _url, self.subscriptionId, fmt.Sprintf(`{"name": "Default", "location": "%s"}`, location)) if err != nil { return err } @@ -281,6 +283,18 @@ func (self *SAzureClient) checkParams(body jsonutils.JSONObject, params []string return result, nil } +type AzureErrorDetail struct { + Code string `json:"code,omitempty"` + Message string `json:"message,omitempty"` + Target string `json:"target,omitempty"` +} + +type AzureError struct { + Code string `json:"code,omitempty"` + Details []AzureErrorDetail `json:"details,omitempty"` + Message string `json:"message,omitempty"` +} + func (self *SAzureClient) Create(body jsonutils.JSONObject, retVal interface{}) error { cli, err := self.getDefaultClient() if err != nil { @@ -301,11 +315,14 @@ func (self *SAzureClient) Create(body jsonutils.JSONObject, retVal interface{}) return fmt.Errorf("Create Default resourceGroup error?") } url := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s", self.subscriptionId, self.ressourceGroups[0].Name, params["type"], params["name"]) - result, err := jsonRequest(cli, "PUT", self.domain, url, body.String()) + result, err := jsonRequest(cli, "PUT", self.domain, url, self.subscriptionId, body.String()) if err != nil { return err } - return result.Unmarshal(retVal) + if retVal != nil { + return result.Unmarshal(retVal) + } + return nil } func (self *SAzureClient) CheckNameAvailability(Type string, body string) (jsonutils.JSONObject, error) { @@ -317,7 +334,7 @@ func (self *SAzureClient) CheckNameAvailability(Type string, body string) (jsonu 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, body) + return jsonRequest(cli, "POST", self.domain, url, self.subscriptionId, body) } func (self *SAzureClient) Update(body jsonutils.JSONObject, retVal interface{}) error { @@ -326,7 +343,7 @@ func (self *SAzureClient) Update(body jsonutils.JSONObject, retVal interface{}) return err } url, err := body.GetString("id") - result, err := jsonRequest(cli, "PUT", self.domain, url, body.String()) + result, err := jsonRequest(cli, "PUT", self.domain, url, self.subscriptionId, body.String()) if err != nil { return err } @@ -336,8 +353,86 @@ func (self *SAzureClient) Update(body jsonutils.JSONObject, retVal interface{}) return nil } -func jsonRequest(client *autorest.Client, method, domain, baseUrl string, body string) (jsonutils.JSONObject, error) { - return _jsonRequest(client, method, domain, baseUrl, body) +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), "") + 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 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, "") + if err != nil || result.Contains("error") { + return fmt.Errorf("failed to register %s service", serviceType) + } + if state, _ := result.GetString("registrationState"); state == "Registered" { + return nil + } + return waitRegisterComplete(client, domain, subscriptionId, serviceType) +} + +func recoverFromError(client *autorest.Client, domain, subscriptionId string, azureErr AzureError) bool { + switch azureErr.Code { + case "SubscriptionNotRegistered": + services := []string{"Microsoft.Network"} + for _, service := range services { + if err := registerService(client, domain, subscriptionId, service); err != nil { + log.Errorf("register %s error: %v", service, err) + return false + } + } + 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 +} + +func jsonRequest(client *autorest.Client, method, domain, baseUrl string, subscriptionId string, body string) (jsonutils.JSONObject, error) { + result, err := _jsonRequest(client, method, domain, baseUrl, body) + if err != nil { + return nil, err + } + if result.Contains("error") { + azureError := AzureError{} + if err := result.Unmarshal(&azureError, "error"); err != nil { + return nil, fmt.Errorf(result.String()) + } + if recoverFromError(client, domain, subscriptionId, azureError) { + return _jsonRequest(client, method, domain, baseUrl, body) + } + log.Errorf("Azure %s request: %s \nbody: %s error: %v", method, baseUrl, body, result.String()) + return nil, fmt.Errorf(result.String()) + } + return result, nil } func waitForComplatetion(client *autorest.Client, req *http.Request, resp *http.Response, timeout time.Duration) (jsonutils.JSONObject, error) { @@ -464,15 +559,7 @@ func _jsonRequest(client *autorest.Client, method, domain, baseURL, body string) return nil, err } _data := strings.Replace(string(data), "\r", "", -1) - result, err = jsonutils.Parse([]byte(_data)) - if err != nil { - return nil, err - } - if result.Contains("error") { - log.Errorf("Azure %s request: %s \nbody: %s error: %v", req.Method, req.URL.String(), body, result.String()) - return nil, fmt.Errorf(result.String()) - } - return result, nil + return jsonutils.Parse([]byte(_data)) } func (self *SAzureClient) UpdateAccount(tenantId, secret, envName string) error { diff --git a/pkg/util/azure/debug.go b/pkg/util/azure/debug.go index 8da73e4983..57beb087d0 100644 --- a/pkg/util/azure/debug.go +++ b/pkg/util/azure/debug.go @@ -9,6 +9,10 @@ import ( "github.com/Azure/go-autorest/autorest" ) +const ( + DEBUG = false +) + func LogRequest() autorest.PrepareDecorator { return func(p autorest.Preparer) autorest.Preparer { return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { diff --git a/pkg/util/azure/service.go b/pkg/util/azure/service.go new file mode 100644 index 0000000000..c5fa17abe7 --- /dev/null +++ b/pkg/util/azure/service.go @@ -0,0 +1,55 @@ +package azure + +import ( + "fmt" +) + +type SServices struct { + Value []SService `json:"value,omitempty"` +} + +type SService struct { + ID string `json:"id,omitempty"` + Namespace string `json:"namespace,omitempty"` + RegistrationState string `json:"registrationState,omitempty"` + ResourceTypes []ResourceType `json:"resourceTypes,omitempty"` +} + +type ResourceType struct { + ApiVersions []string `json:"apiVersions,omitempty"` + Capabilities string `json:"capabilities,omitempty"` + Locations []string `json:"locations,omitempty"` + ResourceType string `json:"locations,omitempty"` +} + +func (self *SRegion) ListServices() ([]SService, error) { + services := []SService{} + return services, self.client.List("providers", &services) +} + +func (self *SRegion) SerciceShow(serviceType string) (*SService, error) { + service := SService{} + return &service, self.client.Get("providers/"+serviceType, []string{}, &service) +} + +func (self *SRegion) serviceOperation(resourceType, operation string) error { + services, err := self.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), "") + return err + } + } + return fmt.Errorf("failed to find namespace: %s", resourceType) +} + +func (self *SRegion) ServiceRegister(resourceType string) error { + return self.serviceOperation(resourceType, "register") +} + +func (self *SRegion) ServiceUnRegister(resourceType string) error { + return self.serviceOperation(resourceType, "unregister") +} diff --git a/pkg/util/azure/shell/service.go b/pkg/util/azure/shell/service.go new file mode 100644 index 0000000000..2f1c26bb5f --- /dev/null +++ b/pkg/util/azure/shell/service.go @@ -0,0 +1,41 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/azure" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type ServiceListOptions struct { + } + shellutils.R(&ServiceListOptions{}, "service-list", "List providers", func(cli *azure.SRegion, args *ServiceListOptions) error { + services, err := cli.ListServices() + if err != nil { + return err + } + printList(services, len(services), 0, 0, []string{}) + return nil + }) + + type ServiceOptions struct { + NAME string `help:"Name for service register"` + } + + shellutils.R(&ServiceOptions{}, "service-register", "Register service", func(cli *azure.SRegion, args *ServiceOptions) error { + return cli.ServiceRegister(args.NAME) + }) + + shellutils.R(&ServiceOptions{}, "service-unregister", "Unregister service", func(cli *azure.SRegion, args *ServiceOptions) error { + return cli.ServiceUnRegister(args.NAME) + }) + + shellutils.R(&ServiceOptions{}, "service-show", "Show service detail", func(cli *azure.SRegion, args *ServiceOptions) error { + service, err := cli.SerciceShow(args.NAME) + if err != nil { + return err + } + printObject(service) + return nil + }) + +} diff --git a/pkg/util/azure/shell/vpc.go b/pkg/util/azure/shell/vpc.go index 6c068fdc0b..552a22c654 100644 --- a/pkg/util/azure/shell/vpc.go +++ b/pkg/util/azure/shell/vpc.go @@ -32,4 +32,23 @@ func init() { printObject(vpc) return nil }) + + shellutils.R(&VpcOptions{}, "vpc-delete", "Delete vpc", func(cli *azure.SRegion, args *VpcOptions) error { + return cli.DeleteVpc(args.ID) + }) + + type VpcCreateOptions struct { + NAME string `help:"vpc Name"` + CIDR string `help:"vpc cidr"` + Desc string `help:"vpc description"` + } + + shellutils.R(&VpcCreateOptions{}, "vpc-create", "Create vpc", func(cli *azure.SRegion, args *VpcCreateOptions) error { + vpc, err := cli.CreateIVpc(args.NAME, args.Desc, args.CIDR) + if err != nil { + return err + } + printObject(vpc) + return nil + }) } diff --git a/pkg/util/azure/vpc.go b/pkg/util/azure/vpc.go index b75d14bbde..07060c4a4c 100644 --- a/pkg/util/azure/vpc.go +++ b/pkg/util/azure/vpc.go @@ -77,7 +77,11 @@ func (self *SVpc) GetCidrBlock() string { } func (self *SVpc) Delete() error { - return self.region.client.Delete(self.ID) + return self.region.DeleteVpc(self.ID) +} + +func (self *SRegion) DeleteVpc(vpcId string) error { + return self.client.Delete(vpcId) } func (self *SVpc) getSecurityGroups() ([]SSecurityGroup, error) {