diff --git a/cloudmanager/client.go b/cloudmanager/client.go index d409975..97d7b4c 100644 --- a/cloudmanager/client.go +++ b/cloudmanager/client.go @@ -98,7 +98,6 @@ func (c *Client) CallAWSInstanceCreate(occmDetails createOCCMDetails) (string, e tags = append(tags, tag) } } - // Specify the details of the instance that you want to create. runInstancesInput := &ec2.RunInstancesInput{ BlockDeviceMappings: []*ec2.BlockDeviceMapping{ @@ -465,8 +464,6 @@ func (c *Client) CallAMIGet(occmDetails createOCCMDetails) (string, error) { } } - log.Print("CallAMIGet ", *result) - return latestAMI, nil } @@ -545,7 +542,21 @@ func (c *Client) CallVNetGetCidr(subscriptionID string, resourceGroup string, vn // CallAWSInstanceGet can be used to make a request to get AWS Instance func (c *Client) CallAWSInstanceGet(occmDetails createOCCMDetails) ([]ec2.Instance, error) { - + if occmDetails.Region == "" { + regions, err := c.CallAWSRegionGet(occmDetails) + if err != nil { + return nil, err + } + var res []ec2.Instance + for _, region := range regions { + regionReservation, err := c.CallAWSGetReservationsForRegion(region) + if err != nil { + return nil, err + } + res = append(res, regionReservation...) + } + return res, nil + } sess := session.Must(session.NewSession(aws.NewConfig().WithRegion(occmDetails.Region))) svc := ec2.New(sess) input := &ec2.DescribeInstancesInput{ @@ -598,6 +609,30 @@ func (c *Client) CallAWSInstanceGet(occmDetails createOCCMDetails) ([]ec2.Instan return res, nil } +// CallAWSRegionGet describe all regions. +func (c *Client) CallAWSRegionGet(occmDetails createOCCMDetails) ([]string, error) { + sess := session.Must(session.NewSession()) + svc := ec2.New(sess) + + result, err := svc.DescribeRegions(nil) + if err != nil { + log.Printf("CallAWSRegionGet error: %#v", err) + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + default: + return nil, aerr + } + } + return nil, err + } + + var res []string + for _, region := range result.Regions { + res = append(res, *region.RegionName) + } + return res, nil +} + // CallAPIMethod can be used to make a request to any CVO/OCCM API method, receiving results as byte func (c *Client) CallAPIMethod(method string, baseURL string, params map[string]interface{}, token string, hostType string) (int, []byte, string, error) { c.initOnce.Do(c.init) @@ -748,3 +783,59 @@ func (c *Client) CallAWSTagDelete(occmDetails createOCCMDetails) error { fmt.Println(result) return nil } + +// CallAWSDescribeInstanceAttribute returns disableAPITermination. +func (c *Client) CallAWSDescribeInstanceAttribute(occmDetails createOCCMDetails) (bool, error) { + sess := session.Must(session.NewSession(aws.NewConfig().WithRegion(occmDetails.Region))) + svc := ec2.New(sess) + input := &ec2.DescribeInstanceAttributeInput{ + Attribute: aws.String("disableApiTermination"), + InstanceId: aws.String(occmDetails.InstanceID), + } + + result, err := svc.DescribeInstanceAttribute(input) + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + default: + return false, aerr + } + } + return false, err + } + disableAPITermination := *result.DisableApiTermination.Value + if err != nil { + return false, err + } + + return disableAPITermination, nil +} + +// CallAWSGetReservationsForRegion gets reservations for a region. +func (c *Client) CallAWSGetReservationsForRegion(region string) ([]ec2.Instance, error) { + + var res []ec2.Instance + + sess := session.Must(session.NewSession(aws.NewConfig().WithRegion(region))) + svc := ec2.New(sess) + input := &ec2.DescribeInstancesInput{} + + result, err := svc.DescribeInstances(input) + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + default: + return nil, aerr + } + } + return nil, err + } + + for _, reservation := range result.Reservations { + for _, instance := range reservation.Instances { + res = append(res, *instance) + } + } + + return res, err +} diff --git a/cloudmanager/occm_aws.go b/cloudmanager/occm_aws.go index c925ed7..784a674 100644 --- a/cloudmanager/occm_aws.go +++ b/cloudmanager/occm_aws.go @@ -6,6 +6,7 @@ import ( "log" "time" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/fatih/structs" ) @@ -365,14 +366,14 @@ func (c *Client) createAWSInstance(occmDetails createOCCMDetails) (string, error return instanceID, nil } -func (c *Client) getAWSInstance(occmDetails createOCCMDetails, id string) (createOCCMDetails, error) { +func (c *Client) getAWSInstance(occmDetails createOCCMDetails, id string) (ec2.Instance, error) { log.Print("getAWSInstance") res, err := c.CallAWSInstanceGet(occmDetails) returnOCCM := createOCCMDetails{} if err != nil { - return createOCCMDetails{}, err + return ec2.Instance{}, nil } log.Printf("getAWSInstance result: %#v", res) for _, instance := range res { @@ -380,11 +381,10 @@ func (c *Client) getAWSInstance(occmDetails createOCCMDetails, id string) (creat returnOCCM.AMI = *instance.ImageId returnOCCM.InstanceID = *instance.InstanceId returnOCCM.InstanceType = *instance.InstanceType - return returnOCCM, nil + return instance, nil } } - - return createOCCMDetails{}, nil + return ec2.Instance{}, nil } func (c *Client) createOCCM(occmDetails createOCCMDetails, proxyCertificates []string) (OCCMMResult, error) { @@ -438,7 +438,6 @@ func (c *Client) checkOCCMStatus() (occmAgent, error) { baseURL := fmt.Sprintf("/agents-mgmt/agent/%sclients", c.ClientID) hostType := "CloudManagerHost" - statusCode, response, _, err := c.CallAPIMethod("GET", baseURL, nil, c.Token, hostType) if err != nil { log.Print("checkOCCMStatus request failed ", statusCode) @@ -572,3 +571,29 @@ func (c *Client) updateOCCM(occmDetails createOCCMDetails, proxyCertificates []s return nil } + +func (c *Client) getCompany() (string, error) { + if c.Token == "" { + accesTokenResult, err := c.getAccessToken() + if err != nil { + return "", err + } + c.Token = accesTokenResult.Token + } + hostType := "CloudManagerHost" + baseURL := "/occm/api/occm/system/about" + statusCode, response, _, err := c.CallAPIMethod("GET", baseURL, nil, c.Token, hostType) + if err != nil { + log.Print("getCompany request failed ", statusCode) + return "", err + } + responseError := apiResponseChecker(statusCode, response, "getCompany") + if responseError != nil { + return "", responseError + } + var f interface{} + json.Unmarshal(response, &f) + m := f.(map[string]interface{}) + siteIdentifier := m["siteIdentifier"].(map[string]interface{}) + return siteIdentifier["company"].(string), nil +} diff --git a/cloudmanager/resource_netapp_cloudmanager_connector_aws.go b/cloudmanager/resource_netapp_cloudmanager_connector_aws.go index b7bf147..fe927fe 100644 --- a/cloudmanager/resource_netapp_cloudmanager_connector_aws.go +++ b/cloudmanager/resource_netapp_cloudmanager_connector_aws.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "log" + "strings" "github.com/hashicorp/terraform/helper/schema" ) @@ -17,7 +18,7 @@ func resourceOCCMAWS() *schema.Resource { Exists: resourceOCCMAWSExists, Update: resourceOCCMAWSUpdate, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: resourceOCCMAWSImport, }, Schema: map[string]*schema.Schema{ @@ -256,10 +257,73 @@ func resourceOCCMAWSRead(d *schema.ResourceData, meta interface{}) error { return err } - if res.InstanceID != id { + if *res.InstanceId != id { return fmt.Errorf("Expected occm ID %v, Response could not find", id) } + if occmDetails.Region == "" { + occmDetails.Region = *res.Placement.AvailabilityZone + occmDetails.Region = occmDetails.Region[:len(occmDetails.Region)-1] + d.Set("region", occmDetails.Region) + } + occmDetails.InstanceID = *res.InstanceId + disableAPITermination, err := client.CallAWSDescribeInstanceAttribute(occmDetails) + if err != nil { + return err + } + d.Set("enable_termination_protection", disableAPITermination) + d.Set("instance_type", res.InstanceType) + d.Set("subnet_id", res.SubnetId) + d.Set("security_group_id", res.SecurityGroups[0].GroupId) + d.Set("key_name", res.KeyName) + iamInstanceProfile := *res.IamInstanceProfile.Arn + slashIndex := strings.Index(iamInstanceProfile, "/") + iamInstanceProfile = iamInstanceProfile[slashIndex+1:] + d.Set("iam_instance_profile_name", iamInstanceProfile) + if _, ok := d.GetOk("ami"); ok { + d.Set("ami", res.ImageId) + } + // The following tags are ignored. + excludedTags := [...]string{"Name", "OCCMInstance", "Owner", "PrincipalId"} + tags := make([]map[string]string, 0) + for _, tag := range res.Tags { + var exclusion bool + for _, exTag := range excludedTags { + if *tag.Key == exTag { + exclusion = true + } + } + if exclusion == false { + tagMap := make(map[string]string) + tagMap["tag_key"] = *tag.Key + tagMap["tag_value"] = *tag.Value + tags = append(tags, tagMap) + } + if *tag.Key == "Name" { + d.Set("name", *tag.Value) + } + } + d.Set("aws_tag", tags) + + if res.PublicIpAddress == nil { + d.Set("associate_public_ip_address", false) + } else { + d.Set("associate_public_ip_address", true) + } + + if v, ok := d.GetOk("client_id"); ok { + client.ClientID = v.(string) + } + + if _, ok := d.GetOk("company"); !ok { + company, err := client.getCompany() + if err != nil { + log.Printf("Error when reading system info from cloudmanager.") + return err + } + d.Set("company", company) + } + return nil } @@ -310,7 +374,7 @@ func resourceOCCMAWSExists(d *schema.ResourceData, meta interface{}) (bool, erro return false, err } - if res.InstanceID != id { + if *res.InstanceId != id { d.SetId("") return false, nil } @@ -360,3 +424,16 @@ func resourceOCCMAWSUpdate(d *schema.ResourceData, meta interface{}) error { } return nil } + +func resourceOCCMAWSImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + parts := strings.Split(d.Id(), ":") + if len(parts) != 2 { + return []*schema.ResourceData{}, fmt.Errorf("Wrong format of resource: %s. Please follow 'client_id/connector_id'", d.Id()) + } + + d.SetId(parts[1]) + d.Set("client_id", parts[0]) + + return []*schema.ResourceData{d}, nil + +} diff --git a/website/docs/r/connector_aws.html.markdown b/website/docs/r/connector_aws.html.markdown index 3a2722b..00e9e64 100644 --- a/website/docs/r/connector_aws.html.markdown +++ b/website/docs/r/connector_aws.html.markdown @@ -81,3 +81,6 @@ The following attributes are exported in addition to the arguments listed above: With netapp-cloudmanager_connector_aws, every resource has a unique ID, but names are not necessarily unique. +## Connector Import +The id used to import is constructed with two attributes: client id and connector id. The format is CLIENT_ID:CONNECTOR_ID +