From a5254a2e6808785ca425ac37ad06f2ce61935276 Mon Sep 17 00:00:00 2001 From: Steven EYCHENNE Date: Fri, 26 May 2017 10:06:24 +0200 Subject: [PATCH 01/35] Add resource to handle customised bare metal where configuration and price are saved in quote. --- softlayer/provider.go | 1 + .../resource_softlayer_bare_metal_quote.go | 397 ++++++++++++++++++ ...esource_softlayer_bare_metal_quote_test.go | 138 ++++++ 3 files changed, 536 insertions(+) create mode 100644 softlayer/resource_softlayer_bare_metal_quote.go create mode 100644 softlayer/resource_softlayer_bare_metal_quote_test.go diff --git a/softlayer/provider.go b/softlayer/provider.go index c331e467a..d14b51789 100644 --- a/softlayer/provider.go +++ b/softlayer/provider.go @@ -55,6 +55,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "softlayer_virtual_guest": resourceSoftLayerVirtualGuest(), "softlayer_bare_metal": resourceSoftLayerBareMetal(), + "softlayer_bare_metal_quote": resourceSoftLayerBareMetalQuote(), "softlayer_ssh_key": resourceSoftLayerSSHKey(), "softlayer_dns_domain_record": resourceSoftLayerDnsDomainRecord(), "softlayer_dns_domain": resourceSoftLayerDnsDomain(), diff --git a/softlayer/resource_softlayer_bare_metal_quote.go b/softlayer/resource_softlayer_bare_metal_quote.go new file mode 100644 index 000000000..cd46af34e --- /dev/null +++ b/softlayer/resource_softlayer_bare_metal_quote.go @@ -0,0 +1,397 @@ +package softlayer + +import ( + "fmt" + "log" + "strconv" + "strings" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/softlayer/softlayer-go/datatypes" + "github.com/softlayer/softlayer-go/filter" + "github.com/softlayer/softlayer-go/services" + "github.com/softlayer/softlayer-go/sl" +) + +func resourceSoftLayerBareMetalQuote() *schema.Resource { + return &schema.Resource{ + Create: resourceSoftLayerBareMetalQuoteCreate, + Read: resourceSoftLayerBareMetalQuoteRead, + Update: resourceSoftLayerBareMetalQuoteUpdate, + Delete: resourceSoftLayerBareMetalQuoteDelete, + Exists: resourceSoftLayerBareMetalQuoteExists, + Importer: &schema.ResourceImporter{}, + + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeInt, + Computed: true, + }, + + "hostname": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DefaultFunc: genId, + DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { + // FIXME: Work around another bug in terraform. + // When a default function is used with an optional property, + // terraform will always execute it on apply, even when the property + // already has a value in the state for it. This causes a false diff. + // Making the property Computed:true does not make a difference. + if strings.HasPrefix(o, "terraformed-") && strings.HasPrefix(n, "terraformed-") { + return true + } + + return o == n + }, + }, + + "domain": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "public_vlan_id": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "public_subnet": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "private_vlan_id": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "private_subnet": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "public_ipv4_address": { + Type: schema.TypeString, + Computed: true, + }, + + "private_ipv4_address": { + Type: schema.TypeString, + Computed: true, + }, + + "ssh_key_ids": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeInt}, + ForceNew: true, + }, + + "user_metadata": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "notes": { + Type: schema.TypeString, + Optional: true, + }, + + "post_install_script_uri": { + Type: schema.TypeString, + Optional: true, + Default: nil, + ForceNew: true, + }, + + "quote_id": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "tags": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + }, + } +} + +func resourceSoftLayerBareMetalQuoteCreate(d *schema.ResourceData, meta interface{}) error { + sess := meta.(ProviderConfig).SoftLayerSession() + orderService := services.GetProductOrderService(sess) + quoteService := services.GetBillingOrderQuoteService(sess) + + order, err := quoteService.Id(d.Get("quote_id").(int)).GetRecalculatedOrderContainer(nil, sl.Bool(false)) + if err != nil { + return fmt.Errorf( + "Encountered problem trying to get the bare metal order template from quote: %s", err) + } + + // Set additional parameters + order.Quantity = sl.Int(1) + order.PresetId = nil + order.Hardware = make([]datatypes.Hardware, 0, 1) + order.Hardware = append( + order.Hardware, + datatypes.Hardware{ + Hostname: sl.String(d.Get("hostname").(string)), + Domain: sl.String(d.Get("domain").(string)), + }, + ) + hardware := datatypes.Hardware{ + Hostname: sl.String(d.Get("hostname").(string)), + Domain: sl.String(d.Get("domain").(string)), + } + + log.Println("[INFO] Ordering bare metal server") + + _, err = orderService.PlaceOrder(&order, sl.Bool(false)) + if err != nil { + return fmt.Errorf("Error ordering bare metal server: %s", err) + } + + log.Printf("[INFO] Bare Metal Server ID: %s", d.Id()) + + // wait for machine availability + bm, err := waitForBareMetalProvision(&hardware, meta) + if err != nil { + return fmt.Errorf( + "Error waiting for bare metal server (%s) to become ready: %s", d.Id(), err) + } + + id := *bm.(datatypes.Hardware).Id + d.SetId(fmt.Sprintf("%d", id)) + + // Set tags + err = setHardwareTags(id, d, meta) + if err != nil { + return err + } + + // Set notes + if d.Get("notes").(string) != "" { + err = setHardwareNotes(id, d, meta) + if err != nil { + return err + } + } + + return resourceSoftLayerBareMetalRead(d, meta) +} + +func resourceSoftLayerBareMetalQuoteRead(d *schema.ResourceData, meta interface{}) error { + service := services.GetHardwareService(meta.(ProviderConfig).SoftLayerSession()) + + id, err := strconv.Atoi(d.Id()) + if err != nil { + return fmt.Errorf("Not a valid ID, must be an integer: %s", err) + } + + result, err := service.Id(id).Mask( + "hostname,domain," + + "primaryIpAddress,primaryBackendIpAddress,privateNetworkOnlyFlag," + + "notes,userData[value],tagReferences[id,tag[name]]," + + "hourlyBillingFlag," + + "datacenter[id,name,longName]," + + "primaryNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed]," + + "primaryBackendNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed]", + ).GetObject() + + if err != nil { + return fmt.Errorf("Error retrieving bare metal server: %s", err) + } + + d.Set("hostname", *result.Hostname) + d.Set("domain", *result.Domain) + + if result.PrimaryIpAddress != nil { + d.Set("public_ipv4_address", *result.PrimaryIpAddress) + } + d.Set("private_ipv4_address", *result.PrimaryBackendIpAddress) + + if result.PrimaryNetworkComponent.NetworkVlan != nil { + d.Set("public_vlan_id", *result.PrimaryNetworkComponent.NetworkVlan.Id) + } + + if result.PrimaryBackendNetworkComponent.NetworkVlan != nil { + d.Set("private_vlan_id", *result.PrimaryBackendNetworkComponent.NetworkVlan.Id) + } + + userData := result.UserData + if len(userData) > 0 && userData[0].Value != nil { + d.Set("user_metadata", *userData[0].Value) + } + + d.Set("notes", sl.Get(result.Notes, nil)) + + tagReferences := result.TagReferences + tagReferencesLen := len(tagReferences) + if tagReferencesLen > 0 { + tags := make([]string, 0, tagReferencesLen) + for _, tagRef := range tagReferences { + tags = append(tags, *tagRef.Tag.Name) + } + d.Set("tags", tags) + } + + connInfo := map[string]string{"type": "ssh"} + connInfo["host"] = *result.PrimaryBackendIpAddress + d.SetConnInfo(connInfo) + + return nil +} + +func resourceSoftLayerBareMetalQuoteUpdate(d *schema.ResourceData, meta interface{}) error { + id, _ := strconv.Atoi(d.Id()) + + if d.HasChange("tags") { + err := setHardwareTags(id, d, meta) + if err != nil { + return err + } + } + + if d.HasChange("notes") { + err := setHardwareNotes(id, d, meta) + if err != nil { + return err + } + } + + return nil +} + +func resourceSoftLayerBareMetalQuoteDelete(d *schema.ResourceData, meta interface{}) error { + sess := meta.(ProviderConfig).SoftLayerSession() + service := services.GetHardwareService(sess) + + id, err := strconv.Atoi(d.Id()) + if err != nil { + return fmt.Errorf("Not a valid ID, must be an integer: %s", err) + } + + _, err = waitForNoBareMetalActiveTransactions(id, meta) + if err != nil { + return fmt.Errorf("Error deleting bare metal server while waiting for zero active transactions: %s", err) + } + + billingItem, err := service.Id(id).GetBillingItem() + if err != nil { + return fmt.Errorf("Error getting billing item for bare metal server: %s", err) + } + + billingItemService := services.GetBillingItemService(sess) + _, err = billingItemService.Id(*billingItem.Id).CancelItem( + sl.Bool(false), sl.Bool(true), sl.String("No longer required"), sl.String("Please cancel this server"), + ) + if err != nil { + return fmt.Errorf("Error canceling the bare metal server (%d): %s", id, err) + } + + return nil +} + +func resourceSoftLayerBareMetalQuoteExists(d *schema.ResourceData, meta interface{}) (bool, error) { + service := services.GetHardwareService(meta.(ProviderConfig).SoftLayerSession()) + + id, err := strconv.Atoi(d.Id()) + if err != nil { + return false, fmt.Errorf("Not a valid ID, must be an integer: %s", err) + } + + result, err := service.Id(id).GetObject() + if err != nil { + if apiErr, ok := err.(sl.Error); !ok || apiErr.StatusCode != 404 { + return false, fmt.Errorf("Error trying to retrieve the Bare Metal server: %s", err) + } + } + + return err == nil && result.Id != nil && *result.Id == id, nil +} + +// Bare metal creation does not return a bare metal object with an Id. +// Have to wait on provision date to become available on server that matches +// hostname and domain. +// http://sldn.softlayer.com/blog/bpotter/ordering-bare-metal-servers-using-softlayer-api +func waitForBareMetalQuoteProvision(d *datatypes.Hardware, meta interface{}) (interface{}, error) { + hostname := *d.Hostname + domain := *d.Domain + log.Printf("Waiting for server (%s.%s) to have to be provisioned", hostname, domain) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"retry", "pending"}, + Target: []string{"provisioned"}, + Refresh: func() (interface{}, string, error) { + service := services.GetAccountService(meta.(ProviderConfig).SoftLayerSession()) + bms, err := service.Filter( + filter.Build( + filter.Path("hardware.hostname").Eq(hostname), + filter.Path("hardware.domain").Eq(domain), + ), + ).Mask("id,provisionDate").GetHardware() + if err != nil { + return false, "retry", nil + } + + if len(bms) == 0 || bms[0].ProvisionDate == nil { + return datatypes.Hardware{}, "pending", nil + } else { + return bms[0], "provisioned", nil + } + }, + Timeout: 4 * time.Hour, + Delay: 30 * time.Second, + MinTimeout: 2 * time.Minute, + } + + return stateConf.WaitForState() +} + +func waitForNoBareMetalQuoteActiveTransactions(id int, meta interface{}) (interface{}, error) { + log.Printf("Waiting for server (%d) to have zero active transactions", id) + service := services.GetHardwareServerService(meta.(ProviderConfig).SoftLayerSession()) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"retry", "active"}, + Target: []string{"idle"}, + Refresh: func() (interface{}, string, error) { + bm, err := service.Id(id).Mask("id,activeTransactionCount").GetObject() + if err != nil { + return false, "retry", nil + } + + if bm.ActiveTransactionCount != nil && *bm.ActiveTransactionCount == 0 { + return bm, "idle", nil + } else { + return bm, "active", nil + } + }, + Timeout: 4 * time.Hour, + Delay: 5 * time.Second, + MinTimeout: 1 * time.Minute, + } + + return stateConf.WaitForState() +} + +// Depends on ressource_softlayer_bare_metal.go setHardwareTags + +// Depends on ressource_softlayer_bare_metal.go setHardwareNotes diff --git a/softlayer/resource_softlayer_bare_metal_quote_test.go b/softlayer/resource_softlayer_bare_metal_quote_test.go new file mode 100644 index 000000000..57ec459fa --- /dev/null +++ b/softlayer/resource_softlayer_bare_metal_quote_test.go @@ -0,0 +1,138 @@ +package softlayer + +import ( + "errors" + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/softlayer/softlayer-go/datatypes" + "github.com/softlayer/softlayer-go/services" + "github.com/softlayer/softlayer-go/sl" +) + +func TestAccSoftLayerBareMetalQuote_Basic(t *testing.T) { + var bareMetal datatypes.Hardware + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSoftLayerBareMetalQuoteDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckSoftLayerBareMetalQuoteConfig_basic, + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testAccCheckSoftLayerBareMetalQuoteExists("softlayer_bare_metal_quote.terraform-acceptance-test-1", &bareMetal), + resource.TestCheckResourceAttr( + "softlayer_bare_metal_quote.terraform-acceptance-test-1", "hostname", "terraform-test"), + resource.TestCheckResourceAttr( + "softlayer_bare_metal_quote.terraform-acceptance-test-1", "domain", "bar.example.com"), + resource.TestCheckResourceAttr( + "softlayer_bare_metal_quote.terraform-acceptance-test-1", "user_metadata", "{\"value\":\"newvalue\"}"), + resource.TestCheckResourceAttr( + "softlayer_bare_metal_quote.terraform-acceptance-test-1", "quote_id", "2179879"), + CheckStringSet( + "softlayer_bare_metal_quote.terraform-acceptance-test-1", + "tags", []string{"collectd"}, + ), + ), + }, + + { + Config: testAccCheckSoftLayerBareMetalQuoteConfig_update, + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testAccCheckSoftLayerBareMetalQuoteExists("softlayer_bare_metal_quote.terraform-acceptance-test-1", &bareMetal), + CheckStringSet( + "softlayer_bare_metal_quote.terraform-acceptance-test-1", + "tags", []string{"mesos-master"}, + ), + ), + }, + }, + }) +} + +func testAccCheckSoftLayerBareMetalQuoteDestroy(s *terraform.State) error { + service := services.GetHardwareService(testAccProvider.Meta().(ProviderConfig).SoftLayerSession()) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "softlayer_bare_metal_quote" { + continue + } + + id, _ := strconv.Atoi(rs.Primary.ID) + + // Try to find the bare metal + _, err := service.Id(id).GetObject() + + // Wait + if err != nil { + if apiErr, ok := err.(sl.Error); !ok || apiErr.StatusCode != 404 { + return fmt.Errorf( + "Error waiting for bare metal (%d) to be destroyed: %s", + id, err) + } + } + } + + return nil +} + +func testAccCheckSoftLayerBareMetalQuoteExists(n string, bareMetal *datatypes.Hardware) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return errors.New("No bare metal ID is set") + } + + id, err := strconv.Atoi(rs.Primary.ID) + + if err != nil { + return err + } + + service := services.GetHardwareService(testAccProvider.Meta().(ProviderConfig).SoftLayerSession()) + bm, err := service.Id(id).GetObject() + if err != nil { + return err + } + + fmt.Printf("The ID is %d", *bm.Id) + + if *bm.Id != id { + return errors.New("Bare metal not found") + } + + *bareMetal = bm + + return nil + } +} + +const testAccCheckSoftLayerBareMetalQuoteConfig_basic = ` +resource "softlayer_bare_metal_quote" "terraform-acceptance-test-1" { + hostname = "terraform-test" + domain = "bar.example.com" + user_metadata = "{\"value\":\"newvalue\"}" + quote_id = 2179879 + tags = ["collectd"] +} +` + +const testAccCheckSoftLayerBareMetalQuoteConfig_update = ` +resource "softlayer_bare_metal_quote" "terraform-acceptance-test-1" { + hostname = "terraform-test" + domain = "bar.example.com" + user_metadata = "{\"value\":\"newvalue\"}" + quote_id = 2179879 + tags = ["mesos-master"] +} +` From 1b27ed33d24e63256b9975fd9553f05a082d8a17 Mon Sep 17 00:00:00 2001 From: Steven EYCHENNE Date: Fri, 23 Jun 2017 14:46:06 +0200 Subject: [PATCH 02/35] Merge bare_metal_quote with bare_metal --- softlayer/provider.go | 1 - softlayer/resource_softlayer_bare_metal.go | 71 ++++++++++++++++------ 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/softlayer/provider.go b/softlayer/provider.go index d14b51789..c331e467a 100644 --- a/softlayer/provider.go +++ b/softlayer/provider.go @@ -55,7 +55,6 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "softlayer_virtual_guest": resourceSoftLayerVirtualGuest(), "softlayer_bare_metal": resourceSoftLayerBareMetal(), - "softlayer_bare_metal_quote": resourceSoftLayerBareMetalQuote(), "softlayer_ssh_key": resourceSoftLayerSSHKey(), "softlayer_dns_domain_record": resourceSoftLayerDnsDomainRecord(), "softlayer_dns_domain": resourceSoftLayerDnsDomain(), diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 34c1f2a71..86b6100a2 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -78,7 +78,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { "datacenter": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, }, @@ -154,7 +154,13 @@ func resourceSoftLayerBareMetal() *schema.Resource { "fixed_config_preset": { Type: schema.TypeString, - Required: true, + Optional: true, + ForceNew: true, + }, + + "quote_id": { + Type: schema.TypeInt, + Optional: true, ForceNew: true, }, @@ -193,10 +199,12 @@ func getBareMetalOrderFromResourceData(d *schema.ResourceData, meta interface{}) NetworkComponents: []datatypes.Network_Component{networkComponent}, PostInstallScriptUri: sl.String(d.Get("post_install_script_uri").(string)), BareMetalInstanceFlag: sl.Int(1), + } - FixedConfigurationPreset: &datatypes.Product_Package_Preset{ - KeyName: sl.String(d.Get("fixed_config_preset").(string)), - }, + if fixed_config_preset, ok := d.GetOk("fixed_config_preset"); ok { + hardware.FixedConfigurationPreset = &datatypes.Product_Package_Preset{ + KeyName: sl.String(fixed_config_preset.(string)), + } } if operatingSystemReferenceCode, ok := d.GetOk("os_reference_code"); ok { @@ -267,23 +275,48 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) return err } - order, err := hwService.GenerateOrderTemplate(&hardware) - if err != nil { - return fmt.Errorf( - "Encountered problem trying to get the bare metal order template: %s", err) - } + quote_id := d.Get("quote_id").(int) + if quote_id > 0 { + quoteService := services.GetBillingOrderQuoteService(meta.(ProviderConfig).SoftLayerSession()) + order, err := quoteService.Id(quote_id).GetRecalculatedOrderContainer(nil, sl.Bool(false)) + if err != nil { + return fmt.Errorf( + "Encountered problem trying to get the bare metal order template from quote: %s", err) + } + order.Quantity = sl.Int(1) + order.PresetId = nil + order.Hardware = make([]datatypes.Hardware, 0, 1) + order.Hardware = append( + order.Hardware, + datatypes.Hardware{ + Hostname: hardware.Hostname, + Domain: hardware.Domain, + }, + ) - // Set image template id if it exists - if rawImageTemplateId, ok := d.GetOk("image_template_id"); ok { - imageTemplateId := rawImageTemplateId.(int) - order.ImageTemplateId = sl.Int(imageTemplateId) - } + log.Println("[INFO] Ordering bare metal server") + _, err = orderService.PlaceOrder(&order, sl.Bool(false)) + if err != nil { + return fmt.Errorf("Error ordering bare metal server: %s\n%+v\n", err, order) + } + } else { + order, err := hwService.GenerateOrderTemplate(&hardware) + if err != nil { + return fmt.Errorf( + "Encountered problem trying to get the bare metal order template: %s", err) + } - log.Println("[INFO] Ordering bare metal server") + // Set image template id if it exists + if rawImageTemplateId, ok := d.GetOk("image_template_id"); ok { + imageTemplateId := rawImageTemplateId.(int) + order.ImageTemplateId = sl.Int(imageTemplateId) + } - _, err = orderService.PlaceOrder(&order, sl.Bool(false)) - if err != nil { - return fmt.Errorf("Error ordering bare metal server: %s", err) + log.Println("[INFO] Ordering bare metal server") + _, err = orderService.PlaceOrder(&order, sl.Bool(false)) + if err != nil { + return fmt.Errorf("Error ordering bare metal server: %s\n%+v\n", err, order) + } } log.Printf("[INFO] Bare Metal Server ID: %s", d.Id()) From 8643c37f27f367d05a764d4817d59686f9bcc79c Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Tue, 27 Jun 2017 16:50:49 -0400 Subject: [PATCH 03/35] bare_metal resource refactoring. --- softlayer/resource_softlayer_bare_metal.go | 44 +++++++++------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 86b6100a2..81258aa99 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -80,6 +80,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, + Computed: true, }, "public_vlan_id": { @@ -267,8 +268,7 @@ func getBareMetalOrderFromResourceData(d *schema.ResourceData, meta interface{}) func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) error { sess := meta.(ProviderConfig).SoftLayerSession() - hwService := services.GetHardwareService(sess) - orderService := services.GetProductOrderService(sess) + var order datatypes.Container_Product_Order hardware, err := getBareMetalOrderFromResourceData(d, meta) if err != nil { @@ -277,46 +277,38 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) quote_id := d.Get("quote_id").(int) if quote_id > 0 { - quoteService := services.GetBillingOrderQuoteService(meta.(ProviderConfig).SoftLayerSession()) - order, err := quoteService.Id(quote_id).GetRecalculatedOrderContainer(nil, sl.Bool(false)) + // Build a bare metal template from the quote. + order, err = services.GetBillingOrderQuoteService(sess). + Id(quote_id).GetRecalculatedOrderContainer(nil, sl.Bool(false)) if err != nil { return fmt.Errorf( "Encountered problem trying to get the bare metal order template from quote: %s", err) } order.Quantity = sl.Int(1) - order.PresetId = nil order.Hardware = make([]datatypes.Hardware, 0, 1) order.Hardware = append( order.Hardware, - datatypes.Hardware{ - Hostname: hardware.Hostname, - Domain: hardware.Domain, - }, + hardware, ) - - log.Println("[INFO] Ordering bare metal server") - _, err = orderService.PlaceOrder(&order, sl.Bool(false)) - if err != nil { - return fmt.Errorf("Error ordering bare metal server: %s\n%+v\n", err, order) - } } else { - order, err := hwService.GenerateOrderTemplate(&hardware) + // Build a bare metal template from scratch. + order, err = services.GetHardwareService(sess).GenerateOrderTemplate(&hardware) if err != nil { return fmt.Errorf( "Encountered problem trying to get the bare metal order template: %s", err) } + } - // Set image template id if it exists - if rawImageTemplateId, ok := d.GetOk("image_template_id"); ok { - imageTemplateId := rawImageTemplateId.(int) - order.ImageTemplateId = sl.Int(imageTemplateId) - } + // Set image template id if it exists + if rawImageTemplateId, ok := d.GetOk("image_template_id"); ok { + imageTemplateId := rawImageTemplateId.(int) + order.ImageTemplateId = sl.Int(imageTemplateId) + } - log.Println("[INFO] Ordering bare metal server") - _, err = orderService.PlaceOrder(&order, sl.Bool(false)) - if err != nil { - return fmt.Errorf("Error ordering bare metal server: %s\n%+v\n", err, order) - } + log.Println("[INFO] Ordering bare metal server") + _, err = services.GetProductOrderService(sess).PlaceOrder(&order, sl.Bool(true)) + if err != nil { + return fmt.Errorf("Error ordering bare metal server: %s\n%+v\n", err, order) } log.Printf("[INFO] Bare Metal Server ID: %s", d.Id()) From f725476238dc9107459537ed538af7c11a639978 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Fri, 30 Jun 2017 02:30:01 -0400 Subject: [PATCH 04/35] Add a custom bare metal create function. --- softlayer/resource_softlayer_bare_metal.go | 179 +++++++++++++++++- .../softlayer-go/helpers/product/product.go | 38 ++++ 2 files changed, 215 insertions(+), 2 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 81258aa99..769443e45 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -11,6 +11,8 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/softlayer/softlayer-go/datatypes" "github.com/softlayer/softlayer-go/filter" + "github.com/softlayer/softlayer-go/helpers/location" + "github.com/softlayer/softlayer-go/helpers/product" "github.com/softlayer/softlayer-go/services" "github.com/softlayer/softlayer-go/sl" ) @@ -178,6 +180,46 @@ func resourceSoftLayerBareMetal() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, + + "model": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "cpu": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "memory": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "disk_controller": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "disks": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "redundant_power_supply": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, }, } } @@ -290,13 +332,129 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) order.Hardware, hardware, ) - } else { - // Build a bare metal template from scratch. + } else if _, ok := d.GetOk("fixed_config_preset"); ok { + // Build a pre-configured bare metal server order, err = services.GetHardwareService(sess).GenerateOrderTemplate(&hardware) if err != nil { return fmt.Errorf( "Encountered problem trying to get the bare metal order template: %s", err) } + } else { + // Build a custom bare metal server + dc, err := location.GetDatacenterByName(sess, d.Get("datacenter").(string), "id") + if err != nil { + return err + } + + model, ok := d.GetOk("model") + if !ok { + return fmt.Errorf("The attribute 'model' is not defined.") + } + + // 1. Get a package by keyName + pkg, err := product.GetPackageByKeyName(sess, model.(string)) + if err != nil { + return err + } + + // 2. Get all prices for the package + items, err := product.GetPackageProducts(sess, *pkg.Id, "id,categories,capacity,description,units,keyName,prices[id,categories[id,name,categoryCode]]") + if err != nil { + return err + } + + log.Printf("**************** Length of items %d", len(items)) + + // 3. Build price items + disks := d.Get("disks").([]interface{}) + server, err := getItemPriceId(items, "server", d.Get("cpu").(string)) + if err != nil { + return err + } + os, err := getItemPriceId(items, "os", "OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT") + if err != nil { + return err + } + ram, err := getItemPriceId(items, "ram", d.Get("memory").(string)) + if err != nil { + return err + } + diskController, err := getItemPriceId(items, "disk_controller", d.Get("disk_controller").(string)) + if err != nil { + return err + } + disk0, err := getItemPriceId(items, "disk0", disks[0].(string)) + if err != nil { + return err + } + portSpeed, err := getItemPriceId(items, "port_speed", "1_GBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS") + if err != nil { + return err + } + /* + powerSupply, err := getItemPriceId(items, "power_supply", "REDUNDANT_POWER_SUPPLY") + if err != nil { + return err + } + */ + bandwidth, err := getItemPriceId(items, "bandwidth", "BANDWIDTH_20000_GB") + if err != nil { + return err + } + priIpAddress, err := getItemPriceId(items, "pri_ip_addresses", "1_IP_ADDRESS") + if err != nil { + return err + } + remoteManagement, err := getItemPriceId(items, "remote_management", "REBOOT_KVM_OVER_IP") + if err != nil { + return err + } + vpnManagement, err := getItemPriceId(items, "vpn_management", "UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT") + if err != nil { + return err + } + monitoring, err := getItemPriceId(items, "monitoring", "MONITORING_HOST_PING") + if err != nil { + return err + } + notification, err := getItemPriceId(items, "notification", "NOTIFICATION_EMAIL_AND_TICKET") + if err != nil { + return err + } + response, err := getItemPriceId(items, "response", "AUTOMATED_NOTIFICATION") + if err != nil { + return err + } + vulnerabilityScanner, err := getItemPriceId(items, "vulnerability_scanner", "NESSUS_VULNERABILITY_ASSESSMENT_REPORTING") + if err != nil { + return err + } + order = datatypes.Container_Product_Order{ + Quantity: sl.Int(1), + Hardware: []datatypes.Hardware{{ + Hostname: sl.String(d.Get("hostname").(string)), + Domain: sl.String(d.Get("domain").(string)), + }}, + Location: sl.String(strconv.Itoa(*dc.Id)), + PackageId: pkg.Id, + Prices: []datatypes.Product_Item_Price{ + server, + os, + ram, + diskController, + disk0, + portSpeed, + // powerSupply, + bandwidth, + priIpAddress, + remoteManagement, + vpnManagement, + monitoring, + notification, + response, + vulnerabilityScanner, + }, + } } // Set image template id if it exists @@ -576,3 +734,20 @@ func setHardwareNotes(id int, d *schema.ResourceData, meta interface{}) error { return nil } + +// Example : getItemPriceId(items, 'server', 'INTEL_XEON_2690_2_60') +func getItemPriceId(items []datatypes.Product_Item, categoryCode string, keyName string) (datatypes.Product_Item_Price, error) { + for _, item := range items { + for _, itemCategory := range item.Categories { + if *itemCategory.CategoryCode == categoryCode && *item.KeyName == keyName { + for _, price := range item.Prices { + if price.LocationGroupId == nil { + return datatypes.Product_Item_Price{Id: price.Id}, nil + } + } + } + } + } + return datatypes.Product_Item_Price{}, + fmt.Errorf("Could not find the matching item with categorycode %s and keyName %s", categoryCode, keyName) +} diff --git a/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go b/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go index 958d8cbaf..8387cb758 100644 --- a/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go +++ b/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go @@ -80,6 +80,44 @@ func GetPackageByType( return packages[0], nil } +// GetPackageByKeyName Get the Product_Package which matches the specified +// package keyName +func GetPackageByKeyName( +sess *session.Session, +keyName string, +mask ...string, +) (datatypes.Product_Package, error) { + + objectMask := "id,keyName,name,description,isActive,type[keyName]" + if len(mask) > 0 { + objectMask = mask[0] + } + + service := services.GetProductPackageService(sess) + + // Get package id + packages, err := service. + Mask(objectMask). + Filter( + filter.Build( + filter.Path("keyName").Eq(keyName), + ), + ). + Limit(1). + GetAllObjects() + if err != nil { + return datatypes.Product_Package{}, err + } + + packages = rejectOutletPackages(packages) + + if len(packages) == 0 { + return datatypes.Product_Package{}, fmt.Errorf("No product packages found for %s", keyName) + } + + return packages[0], nil +} + // rejectOutletPackages removes packages whose description or name contains the // string "OUTLET". func rejectOutletPackages(packages []datatypes.Product_Package) []datatypes.Product_Package { From f52dafa99eb94db6c386d1a435c2e2097784478c Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Fri, 30 Jun 2017 22:50:44 -0400 Subject: [PATCH 05/35] Updated provisioning wait time for custom bm. --- softlayer/resource_softlayer_bare_metal.go | 81 +++++++++++++--------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 769443e45..df8a16f33 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -78,6 +78,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, + // Optional and computed when a quote_id is povided. "datacenter": { Type: schema.TypeString, Optional: true, @@ -242,12 +243,9 @@ func getBareMetalOrderFromResourceData(d *schema.ResourceData, meta interface{}) NetworkComponents: []datatypes.Network_Component{networkComponent}, PostInstallScriptUri: sl.String(d.Get("post_install_script_uri").(string)), BareMetalInstanceFlag: sl.Int(1), - } - - if fixed_config_preset, ok := d.GetOk("fixed_config_preset"); ok { - hardware.FixedConfigurationPreset = &datatypes.Product_Package_Preset{ - KeyName: sl.String(fixed_config_preset.(string)), - } + FixedConfigurationPreset: &datatypes.Product_Package_Preset{ + KeyName: sl.String(d.Get("fixed_config_preset").(string)), + }, } if operatingSystemReferenceCode, ok := d.GetOk("os_reference_code"); ok { @@ -311,13 +309,13 @@ func getBareMetalOrderFromResourceData(d *schema.ResourceData, meta interface{}) func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) error { sess := meta.(ProviderConfig).SoftLayerSession() var order datatypes.Container_Product_Order - - hardware, err := getBareMetalOrderFromResourceData(d, meta) - if err != nil { - return err + var err error + quote_id := d.Get("quote_id").(int) + hardware := datatypes.Hardware{ + Hostname: sl.String(d.Get("hostname").(string)), + Domain: sl.String(d.Get("domain").(string)), } - quote_id := d.Get("quote_id").(int) if quote_id > 0 { // Build a bare metal template from the quote. order, err = services.GetBillingOrderQuoteService(sess). @@ -334,6 +332,10 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) ) } else if _, ok := d.GetOk("fixed_config_preset"); ok { // Build a pre-configured bare metal server + hardware, err = getBareMetalOrderFromResourceData(d, meta) + if err != nil { + return err + } order, err = services.GetHardwareService(sess).GenerateOrderTemplate(&hardware) if err != nil { return fmt.Errorf( @@ -341,16 +343,22 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) } } else { // Build a custom bare metal server - dc, err := location.GetDatacenterByName(sess, d.Get("datacenter").(string), "id") - if err != nil { - return err - } - + // Check mandatory attributes of custom bare metal server ordering. model, ok := d.GetOk("model") if !ok { return fmt.Errorf("The attribute 'model' is not defined.") } + datacenter, ok := d.GetOk("datacenter") + if !ok { + return fmt.Errorf("The attribute 'datacenter' is not defined.") + } + + dc, err := location.GetDatacenterByName(sess, datacenter.(string), "id") + if err != nil { + return err + } + // 1. Get a package by keyName pkg, err := product.GetPackageByKeyName(sess, model.(string)) if err != nil { @@ -363,8 +371,6 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) return err } - log.Printf("**************** Length of items %d", len(items)) - // 3. Build price items disks := d.Get("disks").([]interface{}) server, err := getItemPriceId(items, "server", d.Get("cpu").(string)) @@ -383,10 +389,7 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) if err != nil { return err } - disk0, err := getItemPriceId(items, "disk0", disks[0].(string)) - if err != nil { - return err - } + portSpeed, err := getItemPriceId(items, "port_speed", "1_GBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS") if err != nil { return err @@ -431,10 +434,9 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) } order = datatypes.Container_Product_Order{ Quantity: sl.Int(1), - Hardware: []datatypes.Hardware{{ - Hostname: sl.String(d.Get("hostname").(string)), - Domain: sl.String(d.Get("domain").(string)), - }}, + Hardware: []datatypes.Hardware{ + hardware, + }, Location: sl.String(strconv.Itoa(*dc.Id)), PackageId: pkg.Id, Prices: []datatypes.Product_Item_Price{ @@ -442,7 +444,6 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) os, ram, diskController, - disk0, portSpeed, // powerSupply, bandwidth, @@ -455,6 +456,18 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) vulnerabilityScanner, }, } + + // Add prices of disks. + diskLen := len(disks) + if diskLen > 0 { + for i, disk := range disks { + diskPrice, err := getItemPriceId(items, "disk"+strconv.Itoa(i), disk.(string)) + if err != nil { + return err + } + order.Prices = append(order.Prices, diskPrice) + } + } } // Set image template id if it exists @@ -670,9 +683,10 @@ func waitForBareMetalProvision(d *datatypes.Hardware, meta interface{}) (interfa return bms[0], "provisioned", nil } }, - Timeout: 4 * time.Hour, - Delay: 30 * time.Second, - MinTimeout: 2 * time.Minute, + Timeout: 24 * time.Hour, + Delay: 60 * time.Second, + MinTimeout: 2 * time.Minute, + NotFoundChecks: 24 * 60, } return stateConf.WaitForState() @@ -697,9 +711,10 @@ func waitForNoBareMetalActiveTransactions(id int, meta interface{}) (interface{} return bm, "active", nil } }, - Timeout: 4 * time.Hour, - Delay: 5 * time.Second, - MinTimeout: 1 * time.Minute, + Timeout: 24 * time.Hour, + Delay: 60 * time.Second, + MinTimeout: 2 * time.Minute, + NotFoundChecks: 24 * 60, } return stateConf.WaitForState() From 8fd1cec756f8bf1b520750de34a3e3cff186041b Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Sun, 2 Jul 2017 22:56:08 -0400 Subject: [PATCH 06/35] Updated custom bare metal ordering parameters. --- softlayer/resource_softlayer_bare_metal.go | 333 +++++++++++++-------- 1 file changed, 204 insertions(+), 129 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index df8a16f33..bd66a0420 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -331,7 +331,7 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) hardware, ) } else if _, ok := d.GetOk("fixed_config_preset"); ok { - // Build a pre-configured bare metal server + // Build a pre-configured bare metal server template using fixed_config_preset. hardware, err = getBareMetalOrderFromResourceData(d, meta) if err != nil { return err @@ -342,138 +342,18 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) "Encountered problem trying to get the bare metal order template: %s", err) } } else { - // Build a custom bare metal server - // Check mandatory attributes of custom bare metal server ordering. - model, ok := d.GetOk("model") - if !ok { - return fmt.Errorf("The attribute 'model' is not defined.") - } - - datacenter, ok := d.GetOk("datacenter") - if !ok { - return fmt.Errorf("The attribute 'datacenter' is not defined.") - } - - dc, err := location.GetDatacenterByName(sess, datacenter.(string), "id") - if err != nil { - return err - } - - // 1. Get a package by keyName - pkg, err := product.GetPackageByKeyName(sess, model.(string)) - if err != nil { - return err - } - - // 2. Get all prices for the package - items, err := product.GetPackageProducts(sess, *pkg.Id, "id,categories,capacity,description,units,keyName,prices[id,categories[id,name,categoryCode]]") + // Build a custom bare metal server template + order, err = getCustomBareMetalOrder(d, meta) if err != nil { - return err - } - - // 3. Build price items - disks := d.Get("disks").([]interface{}) - server, err := getItemPriceId(items, "server", d.Get("cpu").(string)) - if err != nil { - return err - } - os, err := getItemPriceId(items, "os", "OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT") - if err != nil { - return err - } - ram, err := getItemPriceId(items, "ram", d.Get("memory").(string)) - if err != nil { - return err - } - diskController, err := getItemPriceId(items, "disk_controller", d.Get("disk_controller").(string)) - if err != nil { - return err - } - - portSpeed, err := getItemPriceId(items, "port_speed", "1_GBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS") - if err != nil { - return err - } - /* - powerSupply, err := getItemPriceId(items, "power_supply", "REDUNDANT_POWER_SUPPLY") - if err != nil { - return err - } - */ - bandwidth, err := getItemPriceId(items, "bandwidth", "BANDWIDTH_20000_GB") - if err != nil { - return err - } - priIpAddress, err := getItemPriceId(items, "pri_ip_addresses", "1_IP_ADDRESS") - if err != nil { - return err - } - remoteManagement, err := getItemPriceId(items, "remote_management", "REBOOT_KVM_OVER_IP") - if err != nil { - return err - } - vpnManagement, err := getItemPriceId(items, "vpn_management", "UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT") - if err != nil { - return err - } - monitoring, err := getItemPriceId(items, "monitoring", "MONITORING_HOST_PING") - if err != nil { - return err - } - notification, err := getItemPriceId(items, "notification", "NOTIFICATION_EMAIL_AND_TICKET") - if err != nil { - return err - } - response, err := getItemPriceId(items, "response", "AUTOMATED_NOTIFICATION") - if err != nil { - return err - } - vulnerabilityScanner, err := getItemPriceId(items, "vulnerability_scanner", "NESSUS_VULNERABILITY_ASSESSMENT_REPORTING") - if err != nil { - return err - } - order = datatypes.Container_Product_Order{ - Quantity: sl.Int(1), - Hardware: []datatypes.Hardware{ - hardware, - }, - Location: sl.String(strconv.Itoa(*dc.Id)), - PackageId: pkg.Id, - Prices: []datatypes.Product_Item_Price{ - server, - os, - ram, - diskController, - portSpeed, - // powerSupply, - bandwidth, - priIpAddress, - remoteManagement, - vpnManagement, - monitoring, - notification, - response, - vulnerabilityScanner, - }, - } - - // Add prices of disks. - diskLen := len(disks) - if diskLen > 0 { - for i, disk := range disks { - diskPrice, err := getItemPriceId(items, "disk"+strconv.Itoa(i), disk.(string)) - if err != nil { - return err - } - order.Prices = append(order.Prices, diskPrice) - } + return fmt.Errorf( + "Encountered problem trying to get the custom bare metal order template: %s", err) } } - // Set image template id if it exists - if rawImageTemplateId, ok := d.GetOk("image_template_id"); ok { - imageTemplateId := rawImageTemplateId.(int) - order.ImageTemplateId = sl.Int(imageTemplateId) + order, err = setCommonBareMetalOptions(d, meta, order) + if err != nil { + return fmt.Errorf( + "Encountered problem trying to configure bare metal server options: %s", err) } log.Println("[INFO] Ordering bare metal server") @@ -766,3 +646,198 @@ func getItemPriceId(items []datatypes.Product_Item, categoryCode string, keyName return datatypes.Product_Item_Price{}, fmt.Errorf("Could not find the matching item with categorycode %s and keyName %s", categoryCode, keyName) } + +func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatypes.Container_Product_Order, error) { + // Check mandatory attributes of custom bare metal server ordering. + sess := meta.(ProviderConfig).SoftLayerSession() + model, ok := d.GetOk("model") + if !ok { + return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'model' is not defined.") + } + + datacenter, ok := d.GetOk("datacenter") + if !ok { + return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'datacenter' is not defined.") + } + + dc, err := location.GetDatacenterByName(sess, datacenter.(string), "id") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + + // 1. Get a package by keyName + pkg, err := product.GetPackageByKeyName(sess, model.(string)) + if err != nil { + return datatypes.Container_Product_Order{}, err + } + + // 2. Get all prices for the package + items, err := product.GetPackageProducts(sess, *pkg.Id, "id,categories,capacity,description,units,keyName,prices[id,categories[id,name,categoryCode]]") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + + // 3. Build price items + disks := d.Get("disks").([]interface{}) + server, err := getItemPriceId(items, "server", d.Get("cpu").(string)) + if err != nil { + return datatypes.Container_Product_Order{}, err + } + os, err := getItemPriceId(items, "os", "OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + ram, err := getItemPriceId(items, "ram", d.Get("memory").(string)) + if err != nil { + return datatypes.Container_Product_Order{}, err + } + diskController, err := getItemPriceId(items, "disk_controller", d.Get("disk_controller").(string)) + if err != nil { + return datatypes.Container_Product_Order{}, err + } + + portSpeed, err := getItemPriceId(items, "port_speed", "1_GBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + /* + powerSupply, err := getItemPriceId(items, "power_supply", "REDUNDANT_POWER_SUPPLY") + if err != nil { + return err + } + */ + bandwidth, err := getItemPriceId(items, "bandwidth", "BANDWIDTH_20000_GB") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + priIpAddress, err := getItemPriceId(items, "pri_ip_addresses", "1_IP_ADDRESS") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + remoteManagement, err := getItemPriceId(items, "remote_management", "REBOOT_KVM_OVER_IP") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + vpnManagement, err := getItemPriceId(items, "vpn_management", "UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + monitoring, err := getItemPriceId(items, "monitoring", "MONITORING_HOST_PING") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + notification, err := getItemPriceId(items, "notification", "NOTIFICATION_EMAIL_AND_TICKET") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + response, err := getItemPriceId(items, "response", "AUTOMATED_NOTIFICATION") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + vulnerabilityScanner, err := getItemPriceId(items, "vulnerability_scanner", "NESSUS_VULNERABILITY_ASSESSMENT_REPORTING") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + order := datatypes.Container_Product_Order{ + Quantity: sl.Int(1), + Hardware: []datatypes.Hardware{{ + Hostname: sl.String(d.Get("hostname").(string)), + Domain: sl.String(d.Get("domain").(string)), + }, + }, + Location: sl.String(strconv.Itoa(*dc.Id)), + PackageId: pkg.Id, + Prices: []datatypes.Product_Item_Price{ + server, + os, + ram, + diskController, + portSpeed, + // powerSupply, + bandwidth, + priIpAddress, + remoteManagement, + vpnManagement, + monitoring, + notification, + response, + vulnerabilityScanner, + }, + } + + // Add prices of disks. + diskLen := len(disks) + if diskLen > 0 { + for i, disk := range disks { + diskPrice, err := getItemPriceId(items, "disk"+strconv.Itoa(i), disk.(string)) + if err != nil { + return datatypes.Container_Product_Order{}, err + } + order.Prices = append(order.Prices, diskPrice) + } + } + + return order, nil +} + +func setCommonBareMetalOptions(d *schema.ResourceData, meta interface{}, order datatypes.Container_Product_Order) (datatypes.Container_Product_Order, error) { + public_vlan_id := d.Get("public_vlan_id").(int) + + if public_vlan_id > 0 { + order.Hardware[0].PrimaryNetworkComponent = &datatypes.Network_Component{ + NetworkVlan: &datatypes.Network_Vlan{Id: sl.Int(public_vlan_id)}, + } + } + + private_vlan_id := d.Get("private_vlan_id").(int) + if private_vlan_id > 0 { + order.Hardware[0].PrimaryBackendNetworkComponent = &datatypes.Network_Component{ + NetworkVlan: &datatypes.Network_Vlan{Id: sl.Int(private_vlan_id)}, + } + } + + if public_subnet, ok := d.GetOk("public_subnet"); ok { + subnet := public_subnet.(string) + subnetId, err := getSubnetId(subnet, meta) + if err != nil { + return datatypes.Container_Product_Order{}, fmt.Errorf("Error determining id for subnet %s: %s", subnet, err) + } + + order.Hardware[0].PrimaryNetworkComponent.NetworkVlan.PrimarySubnetId = sl.Int(subnetId) + } + + if private_subnet, ok := d.GetOk("private_subnet"); ok { + subnet := private_subnet.(string) + subnetId, err := getSubnetId(subnet, meta) + if err != nil { + return datatypes.Container_Product_Order{}, fmt.Errorf("Error determining id for subnet %s: %s", subnet, err) + } + + order.Hardware[0].PrimaryBackendNetworkComponent.NetworkVlan.PrimarySubnetId = sl.Int(subnetId) + } + + if userMetadata, ok := d.GetOk("user_metadata"); ok { + order.Hardware[0].UserData = []datatypes.Hardware_Attribute{ + {Value: sl.String(userMetadata.(string))}, + } + } + + // Get configured ssh_keys + ssh_key_ids := d.Get("ssh_key_ids").([]interface{}) + if len(ssh_key_ids) > 0 { + order.Hardware[0].SshKeys = make([]datatypes.Security_Ssh_Key, 0, len(ssh_key_ids)) + for _, ssh_key_id := range ssh_key_ids { + order.Hardware[0].SshKeys = append(order.Hardware[0].SshKeys, datatypes.Security_Ssh_Key{ + Id: sl.Int(ssh_key_id.(int)), + }) + } + } + + // Set image template id if it exists + if rawImageTemplateId, ok := d.GetOk("image_template_id"); ok { + imageTemplateId := rawImageTemplateId.(int) + order.ImageTemplateId = sl.Int(imageTemplateId) + } + + return order, nil +} From 3e7c3ab5cbaebb6494c2f7ee46de0d28d6538beb Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 3 Jul 2017 16:19:34 -0400 Subject: [PATCH 07/35] Added network options for custom baremetal provisioning. --- softlayer/resource_softlayer_bare_metal.go | 67 +++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index bd66a0420..365f87c08 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -78,6 +78,22 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, + // Custom bare metal server only + "redundant_network": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + + // Custom bare metal server only + "unbonded_network": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + // Optional and computed when a quote_id is povided. "datacenter": { Type: schema.TypeString, @@ -696,10 +712,11 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } - portSpeed, err := getItemPriceId(items, "port_speed", "1_GBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS") + portSpeed, err := findNetworkItemPriceId(items, d) if err != nil { return datatypes.Container_Product_Order{}, err } + /* powerSupply, err := getItemPriceId(items, "power_supply", "REDUNDANT_POWER_SUPPLY") if err != nil { @@ -841,3 +858,51 @@ func setCommonBareMetalOptions(d *schema.ResourceData, meta interface{}, order d return order, nil } + +func findNetworkItemPriceId(items []datatypes.Product_Item, d *schema.ResourceData) (datatypes.Product_Item_Price, error) { + networkSpeed := d.Get("network_speed").(int) + redundantNetwork := d.Get("redundant_network").(bool) + unbondedNetwork := d.Get("unbonded_network").(bool) + privateNetworkOnly := d.Get("private_network_only").(bool) + + networkSpeedStr := "_MBPS_" + redundantNetworkStr := "" + unbondedNetworkStr := "" + + if networkSpeed < 1000 { + networkSpeedStr = strconv.Itoa(networkSpeed) + networkSpeedStr + } else { + networkSpeedStr = strconv.Itoa(networkSpeed/1000) + "_GBPS" + } + if redundantNetwork { + redundantNetworkStr = "_REDUNDANT" + } + + if unbondedNetwork { + unbondedNetworkStr = "_UNBONDED" + } + + for _, item := range items { + for _, itemCategory := range item.Categories { + if *itemCategory.CategoryCode == "port_speed" && + strings.HasPrefix(*item.KeyName, networkSpeedStr) && + strings.Contains(*item.KeyName, redundantNetworkStr) && + strings.Contains(*item.KeyName, unbondedNetworkStr) { + if (privateNetworkOnly && strings.Contains(*item.KeyName, "_PUBLIC_PRIVATE")) || + (!privateNetworkOnly && !strings.Contains(*item.KeyName, "_PUBLIC_PRIVATE")) || + (!unbondedNetwork && strings.Contains(*item.KeyName, "_UNBONDED")) || + !redundantNetwork && strings.Contains(*item.KeyName, "_REDUNDANT") { + break + } + for _, price := range item.Prices { + if price.LocationGroupId == nil { + return datatypes.Product_Item_Price{Id: price.Id}, nil + } + } + } + } + } + return datatypes.Product_Item_Price{}, + fmt.Errorf("Could not find the network with %s, %s, %s, and private_network_only = %t", + networkSpeedStr, redundantNetworkStr, unbondedNetworkStr, privateNetworkOnly) +} From ce6f8682c0f2abf8c3cddf7d8a44659fd0c9c153 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 3 Jul 2017 20:16:44 -0400 Subject: [PATCH 08/35] Added redundant_power_supply option. --- softlayer/resource_softlayer_bare_metal.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 365f87c08..84905b93f 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -694,7 +694,6 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype } // 3. Build price items - disks := d.Get("disks").([]interface{}) server, err := getItemPriceId(items, "server", d.Get("cpu").(string)) if err != nil { return datatypes.Container_Product_Order{}, err @@ -717,16 +716,12 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } - /* - powerSupply, err := getItemPriceId(items, "power_supply", "REDUNDANT_POWER_SUPPLY") - if err != nil { - return err - } - */ bandwidth, err := getItemPriceId(items, "bandwidth", "BANDWIDTH_20000_GB") if err != nil { return datatypes.Container_Product_Order{}, err } + + // Other common basic options priIpAddress, err := getItemPriceId(items, "pri_ip_addresses", "1_IP_ADDRESS") if err != nil { return datatypes.Container_Product_Order{}, err @@ -755,6 +750,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype if err != nil { return datatypes.Container_Product_Order{}, err } + order := datatypes.Container_Product_Order{ Quantity: sl.Int(1), Hardware: []datatypes.Hardware{{ @@ -770,7 +766,6 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype ram, diskController, portSpeed, - // powerSupply, bandwidth, priIpAddress, remoteManagement, @@ -783,6 +778,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype } // Add prices of disks. + disks := d.Get("disks").([]interface{}) diskLen := len(disks) if diskLen > 0 { for i, disk := range disks { @@ -794,6 +790,15 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype } } + // Add redundant power supply + if d.Get("redundant_power_supply").(bool) { + powerSupply, err := getItemPriceId(items, "power_supply", "REDUNDANT_POWER_SUPPLY") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + order.Prices = append(order.Prices, powerSupply) + } + return order, nil } From e96a4dba42ea1da14f380afa04f3e25c49379cac Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 3 Jul 2017 21:20:18 -0400 Subject: [PATCH 09/35] Added memory options for custom bare metal server. --- softlayer/resource_softlayer_bare_metal.go | 32 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 84905b93f..9b6c85ee2 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -213,7 +213,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { }, "memory": { - Type: schema.TypeString, + Type: schema.TypeInt, Optional: true, ForceNew: true, Computed: true, @@ -702,10 +702,12 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype if err != nil { return datatypes.Container_Product_Order{}, err } - ram, err := getItemPriceId(items, "ram", d.Get("memory").(string)) + + ram, err := findMemoryItemPriceId(items, d) if err != nil { return datatypes.Container_Product_Order{}, err } + diskController, err := getItemPriceId(items, "disk_controller", d.Get("disk_controller").(string)) if err != nil { return datatypes.Container_Product_Order{}, err @@ -721,7 +723,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } - // Other common basic options + // Other common default options priIpAddress, err := getItemPriceId(items, "pri_ip_addresses", "1_IP_ADDRESS") if err != nil { return datatypes.Container_Product_Order{}, err @@ -911,3 +913,27 @@ func findNetworkItemPriceId(items []datatypes.Product_Item, d *schema.ResourceDa fmt.Errorf("Could not find the network with %s, %s, %s, and private_network_only = %t", networkSpeedStr, redundantNetworkStr, unbondedNetworkStr, privateNetworkOnly) } + +func findMemoryItemPriceId(items []datatypes.Product_Item, d *schema.ResourceData) (datatypes.Product_Item_Price, error) { + memory := d.Get("memory").(int) + memoryStr := "RAM_" + strconv.Itoa(memory) + "_GB" + availableMemories := "" + + for _, item := range items { + for _, itemCategory := range item.Categories { + if *itemCategory.CategoryCode == "ram" { + availableMemories = availableMemories + *item.KeyName + "(" + *item.Description + ")" + ", " + if strings.HasPrefix(*item.KeyName, memoryStr) { + for _, price := range item.Prices { + if price.LocationGroupId == nil { + return datatypes.Product_Item_Price{Id: price.Id}, nil + } + } + } + } + } + } + + return datatypes.Product_Item_Price{}, + fmt.Errorf("Could not find the price item for %d GB memory. Available items are %s", memory, availableMemories) +} From 9573d314c2abd97d79061537178d0835354ebe47 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 3 Jul 2017 22:56:27 -0400 Subject: [PATCH 10/35] Added an os_reference_code option to custom bare metal server. --- softlayer/resource_softlayer_bare_metal.go | 28 ++++++++++++++----- .../softlayer-go/helpers/product/product.go | 2 +- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 9b6c85ee2..902e53aa8 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -648,24 +648,29 @@ func setHardwareNotes(id int, d *schema.ResourceData, meta interface{}) error { // Example : getItemPriceId(items, 'server', 'INTEL_XEON_2690_2_60') func getItemPriceId(items []datatypes.Product_Item, categoryCode string, keyName string) (datatypes.Product_Item_Price, error) { + availableItems := "" for _, item := range items { for _, itemCategory := range item.Categories { - if *itemCategory.CategoryCode == categoryCode && *item.KeyName == keyName { - for _, price := range item.Prices { - if price.LocationGroupId == nil { - return datatypes.Product_Item_Price{Id: price.Id}, nil + if *itemCategory.CategoryCode == categoryCode { + availableItems = availableItems + *item.KeyName + " ( " + *item.Description + " ) , " + if *item.KeyName == keyName { + for _, price := range item.Prices { + if price.LocationGroupId == nil { + return datatypes.Product_Item_Price{Id: price.Id}, nil + } } } } } } return datatypes.Product_Item_Price{}, - fmt.Errorf("Could not find the matching item with categorycode %s and keyName %s", categoryCode, keyName) + fmt.Errorf("Could not find the matching item with categorycode %s and keyName %s. Available items are %s", categoryCode, keyName, availableItems) } func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatypes.Container_Product_Order, error) { - // Check mandatory attributes of custom bare metal server ordering. sess := meta.(ProviderConfig).SoftLayerSession() + + // Check mandatory attributes of custom bare metal server ordering. model, ok := d.GetOk("model") if !ok { return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'model' is not defined.") @@ -676,6 +681,11 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'datacenter' is not defined.") } + osReferenceCode, ok := d.GetOk("os_reference_code") + if !ok { + return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'os_reference_code' is not defined.") + } + dc, err := location.GetDatacenterByName(sess, datacenter.(string), "id") if err != nil { return datatypes.Container_Product_Order{}, err @@ -687,6 +697,10 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } + if pkg.Id == nil { + return datatypes.Container_Product_Order{}, err + } + // 2. Get all prices for the package items, err := product.GetPackageProducts(sess, *pkg.Id, "id,categories,capacity,description,units,keyName,prices[id,categories[id,name,categoryCode]]") if err != nil { @@ -698,7 +712,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype if err != nil { return datatypes.Container_Product_Order{}, err } - os, err := getItemPriceId(items, "os", "OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT") + os, err := getItemPriceId(items, "os", osReferenceCode.(string)) if err != nil { return datatypes.Container_Product_Order{}, err } diff --git a/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go b/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go index 8387cb758..450967fd5 100644 --- a/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go +++ b/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go @@ -111,7 +111,7 @@ mask ...string, packages = rejectOutletPackages(packages) - if len(packages) == 0 { + if len(packages) == 0 || packages[0].Id == nil { return datatypes.Product_Package{}, fmt.Errorf("No product packages found for %s", keyName) } From 12344932f7189bc55aecdf6f521667a7d88983d5 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Tue, 4 Jul 2017 21:20:04 -0400 Subject: [PATCH 11/35] Added houly billing validation for custom bare metal. --- softlayer/resource_softlayer_bare_metal.go | 54 ++++++++++++++++--- .../softlayer-go/helpers/product/product.go | 38 ------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 902e53aa8..dde254fd9 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -14,6 +14,7 @@ import ( "github.com/softlayer/softlayer-go/helpers/location" "github.com/softlayer/softlayer-go/helpers/product" "github.com/softlayer/softlayer-go/services" + "github.com/softlayer/softlayer-go/session" "github.com/softlayer/softlayer-go/sl" ) @@ -669,6 +670,10 @@ func getItemPriceId(items []datatypes.Product_Item, categoryCode string, keyName func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatypes.Container_Product_Order, error) { sess := meta.(ProviderConfig).SoftLayerSession() + // Validate attributes for custom bare metal server ordering. + if d.Get("hourly_billing").(bool) { + return datatypes.Container_Product_Order{}, fmt.Errorf("Custom bare metal server only supports monthly billing.") + } // Check mandatory attributes of custom bare metal server ordering. model, ok := d.GetOk("model") @@ -691,8 +696,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } - // 1. Get a package by keyName - pkg, err := product.GetPackageByKeyName(sess, model.(string)) + pkg, err := getPackageByModel(sess, model.(string)) if err != nil { return datatypes.Container_Product_Order{}, err } @@ -722,11 +726,6 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } - diskController, err := getItemPriceId(items, "disk_controller", d.Get("disk_controller").(string)) - if err != nil { - return datatypes.Container_Product_Order{}, err - } - portSpeed, err := findNetworkItemPriceId(items, d) if err != nil { return datatypes.Container_Product_Order{}, err @@ -780,7 +779,6 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype server, os, ram, - diskController, portSpeed, bandwidth, priIpAddress, @@ -793,6 +791,15 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype }, } + // Add disk controller + if dc, ok := d.GetOk("disk_controller"); ok { + diskController, err := getItemPriceId(items, "disk_controller", dc.(string)) + if err != nil { + return datatypes.Container_Product_Order{}, err + } + order.Prices = append(order.Prices, diskController) + } + // Add prices of disks. disks := d.Get("disks").([]interface{}) diskLen := len(disks) @@ -951,3 +958,34 @@ func findMemoryItemPriceId(items []datatypes.Product_Item, d *schema.ResourceDat return datatypes.Product_Item_Price{}, fmt.Errorf("Could not find the price item for %d GB memory. Available items are %s", memory, availableMemories) } + +func getPackageByModel(sess *session.Session, model string) (datatypes.Product_Package, error) { + objectMask := "id,keyName,name,description,isActive,type[keyName]" + service := services.GetProductPackageService(sess) + availableModels := "" + + // Get package id + packages, err := service.Mask(objectMask). + Filter( + filter.Build( + filter.Path("type.keyName").Eq("BARE_METAL_CPU"), + ), + ).GetAllObjects() + if err != nil { + return datatypes.Product_Package{}, err + } + + for _, pkg := range packages { + availableModels = availableModels + *pkg.KeyName // + " ( " + *pkg.Description + " ), " + if pkg.Description != nil { + availableModels = availableModels + " ( " + *pkg.Description + " ), " + } else { + availableModels = availableModels + ", " + } + if *pkg.KeyName == model { + return pkg, nil + } + } + + return datatypes.Product_Package{}, fmt.Errorf("No custom bare metal model for %s. Available model(s) is(are) %s", model, availableModels) +} diff --git a/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go b/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go index 450967fd5..958d8cbaf 100644 --- a/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go +++ b/vendor/github.com/softlayer/softlayer-go/helpers/product/product.go @@ -80,44 +80,6 @@ func GetPackageByType( return packages[0], nil } -// GetPackageByKeyName Get the Product_Package which matches the specified -// package keyName -func GetPackageByKeyName( -sess *session.Session, -keyName string, -mask ...string, -) (datatypes.Product_Package, error) { - - objectMask := "id,keyName,name,description,isActive,type[keyName]" - if len(mask) > 0 { - objectMask = mask[0] - } - - service := services.GetProductPackageService(sess) - - // Get package id - packages, err := service. - Mask(objectMask). - Filter( - filter.Build( - filter.Path("keyName").Eq(keyName), - ), - ). - Limit(1). - GetAllObjects() - if err != nil { - return datatypes.Product_Package{}, err - } - - packages = rejectOutletPackages(packages) - - if len(packages) == 0 || packages[0].Id == nil { - return datatypes.Product_Package{}, fmt.Errorf("No product packages found for %s", keyName) - } - - return packages[0], nil -} - // rejectOutletPackages removes packages whose description or name contains the // string "OUTLET". func rejectOutletPackages(packages []datatypes.Product_Package) []datatypes.Product_Package { From dc772b602708a68bd5ed93675201bc2b68c56dd2 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Wed, 5 Jul 2017 22:09:12 -0400 Subject: [PATCH 12/35] Added raid and bandwidth configuration to custom bare metal server. --- softlayer/resource_softlayer_bare_metal.go | 45 +++++++++++++++------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index dde254fd9..0ebeb459a 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -95,6 +95,13 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, + // Custom bare metal server only + "public_bandwidth": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + // Optional and computed when a quote_id is povided. "datacenter": { Type: schema.TypeString, @@ -220,16 +227,16 @@ func resourceSoftLayerBareMetal() *schema.Resource { Computed: true, }, - "disk_controller": { - Type: schema.TypeString, + "raid": { + Type: schema.TypeInt, Optional: true, ForceNew: true, - Computed: true, }, "disks": { Type: schema.TypeList, Optional: true, + ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, }, @@ -665,7 +672,7 @@ func getItemPriceId(items []datatypes.Product_Item, categoryCode string, keyName } } return datatypes.Product_Item_Price{}, - fmt.Errorf("Could not find the matching item with categorycode %s and keyName %s. Available items are %s", categoryCode, keyName, availableItems) + fmt.Errorf("Could not find the matching item with categorycode %s and keyName %s. Available item(s) is(are) %s", categoryCode, keyName, availableItems) } func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatypes.Container_Product_Order, error) { @@ -730,12 +737,12 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype if err != nil { return datatypes.Container_Product_Order{}, err } - - bandwidth, err := getItemPriceId(items, "bandwidth", "BANDWIDTH_20000_GB") - if err != nil { - return datatypes.Container_Product_Order{}, err - } - + /* + bandwidth, err := getItemPriceId(items, "bandwidth", "BANDWIDTH_20000_GB") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + */ // Other common default options priIpAddress, err := getItemPriceId(items, "pri_ip_addresses", "1_IP_ADDRESS") if err != nil { @@ -780,7 +787,6 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype os, ram, portSpeed, - bandwidth, priIpAddress, remoteManagement, vpnManagement, @@ -792,14 +798,25 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype } // Add disk controller - if dc, ok := d.GetOk("disk_controller"); ok { - diskController, err := getItemPriceId(items, "disk_controller", dc.(string)) + if raid, ok := d.GetOk("raid"); ok { + raidStr := "DISK_CONTROLLER_RAID_" + strconv.Itoa(raid.(int)) + diskController, err := getItemPriceId(items, "disk_controller", raidStr) if err != nil { return datatypes.Container_Product_Order{}, err } order.Prices = append(order.Prices, diskController) } + // Add public bandwidth + if publicBandwidth, ok := d.GetOk("public_bandwidth"); ok { + publicBandwidthStr := "BANDWIDTH_" + strconv.Itoa(publicBandwidth.(int)) + "_GB" + bandwidth, err := getItemPriceId(items, "bandwidth", publicBandwidthStr) + if err != nil { + return datatypes.Container_Product_Order{}, err + } + order.Prices = append(order.Prices, bandwidth) + } + // Add prices of disks. disks := d.Get("disks").([]interface{}) diskLen := len(disks) @@ -976,7 +993,7 @@ func getPackageByModel(sess *session.Session, model string) (datatypes.Product_P } for _, pkg := range packages { - availableModels = availableModels + *pkg.KeyName // + " ( " + *pkg.Description + " ), " + availableModels = availableModels + *pkg.KeyName if pkg.Description != nil { availableModels = availableModels + " ( " + *pkg.Description + " ), " } else { From 936db237f27cd9fbb5bacfb47fc961d5f8eb0943 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Thu, 6 Jul 2017 14:44:39 -0400 Subject: [PATCH 13/35] Added os, powersupply, network, memory attributes to the bare metal read func. --- softlayer/resource_softlayer_bare_metal.go | 86 +++++++++++++++++++--- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 0ebeb459a..df979f52b 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -102,7 +102,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, - // Optional and computed when a quote_id is povided. + // Computed when a quote_id is povided. "datacenter": { Type: schema.TypeString, Optional: true, @@ -178,18 +178,40 @@ func resourceSoftLayerBareMetal() *schema.Resource { Optional: true, Default: nil, ForceNew: true, + DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { + // post_install_script_uri is only used for bare metal server ordering. + if d.State() == nil { + return false + } + return true + }, }, + // pre-configured bare metal server only "fixed_config_preset": { Type: schema.TypeString, Optional: true, ForceNew: true, + DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { + // fixed_config_preset is only used for pre-configured bare metal server ordering. + if d.State() == nil { + return false + } + return true + }, }, "quote_id": { Type: schema.TypeInt, Optional: true, ForceNew: true, + DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { + // quote_id is only used for bare metal server ordering with the quote. + if d.State() == nil { + return false + } + return true + }, }, "image_template_id": { @@ -206,20 +228,35 @@ func resourceSoftLayerBareMetal() *schema.Resource { Set: schema.HashString, }, + // Custom bare metal server only "model": { Type: schema.TypeString, Optional: true, ForceNew: true, - Computed: true, + DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { + // model is only used for custom bare metal server ordering. + if d.State() == nil { + return false + } + return true + }, }, + // Custom bare metal server only "cpu": { Type: schema.TypeString, Optional: true, ForceNew: true, - Computed: true, + DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { + // cpu is only used for custom bare metal server ordering. + if d.State() == nil { + return false + } + return true + }, }, + // Custom bare metal server only "memory": { Type: schema.TypeInt, Optional: true, @@ -227,12 +264,14 @@ func resourceSoftLayerBareMetal() *schema.Resource { Computed: true, }, + // Custom bare metal server only "raid": { Type: schema.TypeInt, Optional: true, ForceNew: true, }, + // Custom bare metal server only "disks": { Type: schema.TypeList, Optional: true, @@ -240,6 +279,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, + // Custom bare metal server only "redundant_power_supply": { Type: schema.TypeBool, Optional: true, @@ -381,7 +421,7 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) } log.Println("[INFO] Ordering bare metal server") - _, err = services.GetProductOrderService(sess).PlaceOrder(&order, sl.Bool(true)) + _, err = services.GetProductOrderService(sess).PlaceOrder(&order, sl.Bool(false)) if err != nil { return fmt.Errorf("Error ordering bare metal server: %s\n%+v\n", err, order) } @@ -430,7 +470,10 @@ func resourceSoftLayerBareMetalRead(d *schema.ResourceData, meta interface{}) er "hourlyBillingFlag," + "datacenter[id,name,longName]," + "primaryNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed]," + - "primaryBackendNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed]", + "primaryBackendNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed]," + + "memoryCapacity,powerSupplyCount,bandwidthAllocation," + + "operatingSystem[softwareLicense[softwareDescription[referenceCode]]]," + + "backendNetworkComponentCount,primaryBackendNetworkComponent[networkVlanTrunkCount]", ).GetObject() if err != nil { @@ -467,6 +510,32 @@ func resourceSoftLayerBareMetalRead(d *schema.ResourceData, meta interface{}) er } d.Set("notes", sl.Get(result.Notes, nil)) + d.Set("memory", *result.MemoryCapacity) + + if *result.PowerSupplyCount == 2 { + d.Set("redundant_power_supply", true) + } else { + d.Set("redundant_power_supply", false) + } + + d.Set("public_bandwidth", int(*result.BandwidthAllocation)) + + d.Set("redundant_network", false) + d.Set("unbonded_network", false) + if *result.BackendNetworkComponentCount > 2 && result.PrimaryBackendNetworkComponent != nil { + if *result.PrimaryBackendNetworkComponent.NetworkVlanTrunkCount > 0 { + d.Set("redundant_network", true) + } else { + d.Set("unbonded_network", true) + } + } + + if result.OperatingSystem != nil && + result.OperatingSystem.SoftwareLicense != nil && + result.OperatingSystem.SoftwareLicense.SoftwareDescription != nil && + result.OperatingSystem.SoftwareLicense.SoftwareDescription.ReferenceCode != nil { + d.Set("os_reference_code", *result.OperatingSystem.SoftwareLicense.SoftwareDescription.ReferenceCode) + } tagReferences := result.TagReferences tagReferencesLen := len(tagReferences) @@ -737,12 +806,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype if err != nil { return datatypes.Container_Product_Order{}, err } - /* - bandwidth, err := getItemPriceId(items, "bandwidth", "BANDWIDTH_20000_GB") - if err != nil { - return datatypes.Container_Product_Order{}, err - } - */ + // Other common default options priIpAddress, err := getItemPriceId(items, "pri_ip_addresses", "1_IP_ADDRESS") if err != nil { From 8fd20dde814a4cea08b9024fec5680a574bcc163 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Thu, 6 Jul 2017 20:56:36 -0400 Subject: [PATCH 14/35] Updated doc for bare metal server. --- docs/resources/softlayer_bare_metal.md | 26 ++++--- softlayer/resource_softlayer_bare_metal.go | 79 ++++++++-------------- 2 files changed, 46 insertions(+), 59 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index f13e81fa7..a204888c9 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -1,12 +1,13 @@ # `softlayer_bare_metal` -Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. +Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. It provides three different ways to create bare metal servers. + ```hcl # Create a new bare metal -resource "softlayer_bare_metal" "twc_terraform_sample" { - hostname = "twc-terraform-sample-name" - domain = "bar.example.com" +resource "softlayer_bare_metal" "pre-configured-bm1" { + hostname = "pre-configured-bm1" + domain = "example.com" os_reference_code = "UBUNTU_16_64" datacenter = "dal01" network_speed = 100 # Optional @@ -38,16 +39,21 @@ The following arguments are supported: * **Required** * `datacenter` | *string* * Specifies which datacenter the instance is to be provisioned in. - * **Required** + * Quote has `datacenter` information already, so if `quote_id` is used, omit `datacenter` attribute. + * *Optional* * `fixed_config_preset` | *string* - * The configuration preset that the bare metal server will be provisioned with. This governs the type of cpu, number of cores, amount of ram, and hard drives which the bare metal server will have. [Take a look at the available presets](https://api.softlayer.com/rest/v3/SoftLayer_Hardware/getCreateObjectOptions.json) (use your api key as the password), and find the key called _fixedConfigurationPresets_. Under that, the presets will be identified by the *keyName*s. - * **Required** + * The configuration preset that the pre-set configuration bare metal server will be provisioned with. This governs the type of cpu, number of cores, amount of ram, and hard drives which the bare metal server will have. [Take a look at the available presets](https://api.softlayer.com/rest/v3/SoftLayer_Hardware/getCreateObjectOptions.json) (use your api key as the password), and find the key called _fixedConfigurationPresets_. Under that, the presets will be identified by the *keyName*s. + * Define this attribute for pre-set configuration bare metal server provisioning. + * *Optional* * `hourly_billing` | *boolean* * Specifies the billing type for the instance. When true the computing instance will be billed on hourly usage, otherwise it will be billed on a monthly basis. + * Only pre-set configuration bare metal servers support hourly billing. * *Default*: true * *Optional* * `os_reference_code` | *string* - * An operating system reference code that will be used to provision the computing instance. [Get a complete list of the os reference codes available](https://api.softlayer.com/rest/v3/SoftLayer_Virtual_Guest_Block_Device_Template_Group/getVhdImportSoftwareDescriptions.json?objectMask=referenceCode) (use your api key as the password). + * An operating system reference code that will be used to provision the computing instance. + * [Get a complete list of the os reference codes available for pre-set configuration bare metal servers](https://api.softlayer.com/rest/v3/SoftLayer_Virtual_Guest_Block_Device_Template_Group/getVhdImportSoftwareDescriptions.json?objectMask=referenceCode) (use your api key as the password). + * [Get a complete list of the os reference codes available for custom bare metal servers]() (use your api key as the password). * *Optional* * **Conflicts with** `image_template_id`. * `image_template_id` | *int* @@ -67,15 +73,19 @@ The following arguments are supported: * *Optional* * `public_vlan_id` | *int* * Public VLAN which is to be used for the public network interface of the instance. Accepted values can be found [here](https://control.softlayer.com/network/vlans). Click on the desired VLAN and note the id number in the URL. + * Only custom bare metal servers support this attribute. * *Optional* * `private_vlan_id` | *int* * Private VLAN which is to be used for the private network interface of the instance. Accepted values can be found [here](https://control.softlayer.com/network/vlans). Click on the desired VLAN and note the id number in the URL. + * Only custom bare metal servers support this attribute. * *Optional* * `public_subnet` | *string* * Public subnet which is to be used for the public network interface of the instance. Accepted values are primary public networks and can be found [here](https://control.softlayer.com/network/subnets). + * Only custom bare metal servers support this attribute. * *Optional* * `private_subnet` | *string* * Private subnet which is to be used for the private network interface of the instance. Accepted values are primary private networks and can be found [here](https://control.softlayer.com/network/subnets). + * Only custom bare metal servers support this attribute. * *Optional* * `user_metadata` | *string* * Arbitrary data to be made available to the computing instance. diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index df979f52b..ec9a98f13 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -174,44 +174,26 @@ func resourceSoftLayerBareMetal() *schema.Resource { }, "post_install_script_uri": { - Type: schema.TypeString, - Optional: true, - Default: nil, - ForceNew: true, - DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { - // post_install_script_uri is only used for bare metal server ordering. - if d.State() == nil { - return false - } - return true - }, + Type: schema.TypeString, + Optional: true, + Default: nil, + ForceNew: true, + DiffSuppressFunc: applyOnce, }, // pre-configured bare metal server only "fixed_config_preset": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { - // fixed_config_preset is only used for pre-configured bare metal server ordering. - if d.State() == nil { - return false - } - return true - }, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, }, "quote_id": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { - // quote_id is only used for bare metal server ordering with the quote. - if d.State() == nil { - return false - } - return true - }, + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, }, "image_template_id": { @@ -230,30 +212,18 @@ func resourceSoftLayerBareMetal() *schema.Resource { // Custom bare metal server only "model": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { - // model is only used for custom bare metal server ordering. - if d.State() == nil { - return false - } - return true - }, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, }, // Custom bare metal server only "cpu": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { - // cpu is only used for custom bare metal server ordering. - if d.State() == nil { - return false - } - return true - }, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, }, // Custom bare metal server only @@ -1070,3 +1040,10 @@ func getPackageByModel(sess *session.Session, model string) (datatypes.Product_P return datatypes.Product_Package{}, fmt.Errorf("No custom bare metal model for %s. Available model(s) is(are) %s", model, availableModels) } + +func applyOnce(k, o, n string, d *schema.ResourceData) bool { + if len(d.Id()) == 0 { + return false + } + return true +} From 3960e2e4b9a46a84b84d8920fd8a848065effea0 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Fri, 7 Jul 2017 02:49:13 -0400 Subject: [PATCH 15/35] Updated doc for bare metal server. --- docs/resources/softlayer_bare_metal.md | 94 +++++++++++++++++++++----- 1 file changed, 76 insertions(+), 18 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index a204888c9..0e263003b 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -2,7 +2,6 @@ Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. It provides three different ways to create bare metal servers. - ```hcl # Create a new bare metal resource "softlayer_bare_metal" "pre-configured-bm1" { @@ -31,19 +30,38 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { The following arguments are supported: +**Common attributes** + * `hostname` | *string* * Hostname for the computing instance. * **Optional** * `domain` | *string* * Domain for the computing instance. * **Required** +* `user_metadata` | *string* + * Arbitrary data to be made available to the computing instance. + * *Optional* +* `notes` | *string* + * A note of up to 1000 characters about the server. + * *Optional* +* `ssh_key_ids` | *array* of numbers + * SSH key _IDs_ to install on the computing instance upon provisioning. + * *Optional* + + **Note:** Don't know the ID(s) for your SSH keys? See [here](https://github.com/softlayer/terraform-provider-softlayer/blob/master/docs/datasources/softlayer_ssh_key.md) for a way to reference your SSH keys by their labels. + +* `post_install_script_uri` | *string* + * As defined in the [SoftLayer_Virtual_Guest_SupplementalCreateObjectOptions](https://sldn.softlayer.com/reference/datatypes/SoftLayer_Virtual_Guest_SupplementalCreateObjectOptions). + * *Optional* +* `tags` | *array* of strings + * Set tags on this bare metal server. The characters permitted are A-Z, 0-9, whitespace, _ (underscore), - (hyphen), . (period), and : (colon). All other characters will be stripped away. + * *Optional* + +**Pre-set configured bare metal server / custom bare metal server attributes** + * `datacenter` | *string* * Specifies which datacenter the instance is to be provisioned in. - * Quote has `datacenter` information already, so if `quote_id` is used, omit `datacenter` attribute. - * *Optional* -* `fixed_config_preset` | *string* - * The configuration preset that the pre-set configuration bare metal server will be provisioned with. This governs the type of cpu, number of cores, amount of ram, and hard drives which the bare metal server will have. [Take a look at the available presets](https://api.softlayer.com/rest/v3/SoftLayer_Hardware/getCreateObjectOptions.json) (use your api key as the password), and find the key called _fixedConfigurationPresets_. Under that, the presets will be identified by the *keyName*s. - * Define this attribute for pre-set configuration bare metal server provisioning. + * It is a mandatory attribute for pre-set configured and custom bare metal servers. * *Optional* * `hourly_billing` | *boolean* * Specifies the billing type for the instance. When true the computing instance will be billed on hourly usage, otherwise it will be billed on a monthly basis. @@ -71,6 +89,16 @@ The following arguments are supported: * Specifies whether or not the instance only has access to the private network. When true this flag specifies that a compute instance is to only have access to the private network. * *Default*: False * *Optional* + +**Pre-set configured bare metal server only attributes** + +* `fixed_config_preset` | *string* + * The configuration preset that the pre-set configuration bare metal server will be provisioned with. This governs the type of cpu, number of cores, amount of ram, and hard drives which the bare metal server will have. [Take a look at the available presets](https://api.softlayer.com/rest/v3/SoftLayer_Hardware/getCreateObjectOptions.json) (use your api key as the password), and find the key called _fixedConfigurationPresets_. Under that, the presets will be identified by the *keyName*s. + * It is a mandatory attribute for pre-set configuration bare metal server provisioning. + * *Optional* + +**Custom bare metal server / Quote based custom bare metal server provisionig attributes** + * `public_vlan_id` | *int* * Public VLAN which is to be used for the public network interface of the instance. Accepted values can be found [here](https://control.softlayer.com/network/vlans). Click on the desired VLAN and note the id number in the URL. * Only custom bare metal servers support this attribute. @@ -87,25 +115,51 @@ The following arguments are supported: * Private subnet which is to be used for the private network interface of the instance. Accepted values are primary private networks and can be found [here](https://control.softlayer.com/network/subnets). * Only custom bare metal servers support this attribute. * *Optional* -* `user_metadata` | *string* - * Arbitrary data to be made available to the computing instance. + +**Custom bare metal server only attributes** + +* `redundant_network` | *boolean* + * If `redundant_network` is `true`, two physical network interfaces will be provided with a bonding configuration. + * *Default*: False * *Optional* -* `notes` | *string* - * A note of up to 1000 characters about the server. +* `unbonded_network` | *boolean* + * If `unbonded_network` is `true`, two physical network interfaces will be provided. + * unbonded_network cannot be `true` when redudant_network is `true`. + * *Default*: False * *Optional* -* `ssh_key_ids` | *array* of numbers - * SSH key _IDs_ to install on the computing instance upon provisioning. +* `public_bandwidth` | *int* + * Public network traffic(GB) per month which can be used without additional charge. + * `public_bandwidth` can be greater than 0 when `private_network_only` is `false` and the server is a monthly based server. + * *Optional* +* `package_key_name` | *string* + * Custom bare metal server's package key name. + * *Optional* +* `process_key_name` | *string* + * Custom bare metal server's process key name. + * *Optional* +* `memory` | *int* + * Amount of memory(GB) for the server. + * *Optional* +* `raid` | *int* + * RAID number for disks. + * *Optional* +* `disks` | *list* + * Array of internal disks. + * *Optional* +* `redundant_power_supply` | *boolean* + * If `redundant_power_supply` is true, additional power supply will be provided. * *Optional* - **Note:** Don't know the ID(s) for your SSH keys? See [here](https://github.com/softlayer/terraform-provider-softlayer/blob/master/docs/datasources/softlayer_ssh_key.md) for a way to reference your SSH keys by their labels. +**Quote based probisioning only attributes** -* `post_install_script_uri` | *string* - * As defined in the [SoftLayer_Virtual_Guest_SupplementalCreateObjectOptions](https://sldn.softlayer.com/reference/datatypes/SoftLayer_Virtual_Guest_SupplementalCreateObjectOptions). - * *Optional* -* `tags` | *array* of strings - * Set tags on this bare metal server. The characters permitted are A-Z, 0-9, whitespace, _ (underscore), - (hyphen), . (period), and : (colon). All other characters will be stripped away. +* `quote_id` | *int* + * Create a pre-set configured bare metal server or custom bare metal server using the quote. + * If quote_id is defined, the terraform uses specifications in the quote to create a bare metal server. + * You can find the quote id by navigating on the portal to _Account > Sales > Quotes_, taking note of the id number in `QUOTE ID` column. * *Optional* + + ## Attributes Reference The following attributes are exported: @@ -113,3 +167,7 @@ The following attributes are exported: * `id` - id of the bare metal. * `public_ipv4_address` - Public IPv4 address of the bare metal server. * `private_ipv4_address` - Private IPv4 address of the bare metal server. +* `model` - Hardware model +* `processor_type` - Processor type +* `processors` - Number of processors. +* `cores` - Number of cores. From 0f8e87f5824d606481cb54fe02787934c3b7867b Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Fri, 7 Jul 2017 13:53:57 -0400 Subject: [PATCH 16/35] Updated doc for bare metal servers. --- docs/resources/softlayer_bare_metal.md | 18 +++---- softlayer/resource_softlayer_bare_metal.go | 60 +++++++++++----------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index 0e263003b..b4ed3fad8 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -118,6 +118,15 @@ The following arguments are supported: **Custom bare metal server only attributes** +* `package_key_name` | *string* + * Custom bare metal server's package key name. This attribute is only used when a new custom bare metal server is created. + * *Optional* +* `process_key_name` | *string* + * Custom bare metal server's process key name. This attribute is only used when a new custom bare metal server is created. + * *Optional* +* `disk_key_names` | *list* + * Array of internal disk key names. This attribute is only used when a new custom bare metal server is created. + * *Optional* * `redundant_network` | *boolean* * If `redundant_network` is `true`, two physical network interfaces will be provided with a bonding configuration. * *Default*: False @@ -131,21 +140,12 @@ The following arguments are supported: * Public network traffic(GB) per month which can be used without additional charge. * `public_bandwidth` can be greater than 0 when `private_network_only` is `false` and the server is a monthly based server. * *Optional* -* `package_key_name` | *string* - * Custom bare metal server's package key name. - * *Optional* -* `process_key_name` | *string* - * Custom bare metal server's process key name. - * *Optional* * `memory` | *int* * Amount of memory(GB) for the server. * *Optional* * `raid` | *int* * RAID number for disks. * *Optional* -* `disks` | *list* - * Array of internal disks. - * *Optional* * `redundant_power_supply` | *boolean* * If `redundant_power_supply` is true, additional power supply will be provided. * *Optional* diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index ec9a98f13..4974b2c3c 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -189,6 +189,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, + // Quote based provisioning only "quote_id": { Type: schema.TypeInt, Optional: true, @@ -210,22 +211,6 @@ func resourceSoftLayerBareMetal() *schema.Resource { Set: schema.HashString, }, - // Custom bare metal server only - "model": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - DiffSuppressFunc: applyOnce, - }, - - // Custom bare metal server only - "cpu": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - DiffSuppressFunc: applyOnce, - }, - // Custom bare metal server only "memory": { Type: schema.TypeInt, @@ -241,20 +226,35 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, - // Custom bare metal server only - "disks": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - // Custom bare metal server only "redundant_power_supply": { Type: schema.TypeBool, Optional: true, Computed: true, }, + + // Custom bare metal server requires key names for baremetal package, process, and disks. + "package_key_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, + }, + + "process_key_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, + }, + + "disk_key_names": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + DiffSuppressFunc: applyOnce, + }, }, } } @@ -391,7 +391,7 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) } log.Println("[INFO] Ordering bare metal server") - _, err = services.GetProductOrderService(sess).PlaceOrder(&order, sl.Bool(false)) + _, err = services.GetProductOrderService(sess).PlaceOrder(&order, sl.Bool(true)) if err != nil { return fmt.Errorf("Error ordering bare metal server: %s\n%+v\n", err, order) } @@ -722,9 +722,9 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype } // Check mandatory attributes of custom bare metal server ordering. - model, ok := d.GetOk("model") + model, ok := d.GetOk("package_key_name") if !ok { - return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'model' is not defined.") + return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'package_key_name' is not defined.") } datacenter, ok := d.GetOk("datacenter") @@ -758,7 +758,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype } // 3. Build price items - server, err := getItemPriceId(items, "server", d.Get("cpu").(string)) + server, err := getItemPriceId(items, "server", d.Get("process_key_name").(string)) if err != nil { return datatypes.Container_Product_Order{}, err } @@ -852,7 +852,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype } // Add prices of disks. - disks := d.Get("disks").([]interface{}) + disks := d.Get("disk_key_names").([]interface{}) diskLen := len(disks) if diskLen > 0 { for i, disk := range disks { @@ -1038,7 +1038,7 @@ func getPackageByModel(sess *session.Session, model string) (datatypes.Product_P } } - return datatypes.Product_Package{}, fmt.Errorf("No custom bare metal model for %s. Available model(s) is(are) %s", model, availableModels) + return datatypes.Product_Package{}, fmt.Errorf("No custom bare metal package key name for %s. Available package key name(s) is(are) %s", model, availableModels) } func applyOnce(k, o, n string, d *schema.ResourceData) bool { From 7947b94245e09d2bbc659a0ec39152c2c1c4dafc Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Fri, 7 Jul 2017 17:54:05 -0400 Subject: [PATCH 17/35] Added storageGroups attribute to bare metal server. --- docs/resources/softlayer_bare_metal.md | 46 ++++++++++++++++++++++ softlayer/resource_softlayer_bare_metal.go | 31 +++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index b4ed3fad8..dfdd850bb 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -2,6 +2,7 @@ Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. It provides three different ways to create bare metal servers. +# Example of Pre-set configured bare metal server ```hcl # Create a new bare metal resource "softlayer_bare_metal" "pre-configured-bm1" { @@ -26,6 +27,45 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { } ``` +# Example of quote based ordering +```hcl +# Create a new bare metal +resource "softlayer_bare_metal" "quote_test" { + hostname = "quote-bm-test" + domain = "example.com" + quote_id = 2209349 +} +``` + +# Example of custom bare metal server +```hcl +resource "softlayer_bare_metal" "custom_bm1" { + package_key_name = "DUAL_E52600_V4_12_DRIVES" + process_key_name = "INTEL_INTEL_XEON_E52620_V4_2_10" + memory = 64 + hostname = "cust-bm" + domain = "ms.com" + os_reference_code = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" + datacenter = "wdc04" + network_speed = 100 + public_bandwidth = 500 + hourly_billing = false + private_network_only = false + unbonded_network = true + user_metadata = "{\"value\":\"newvalue\"}" + public_vlan_id = 12345678 + private_vlan_id = 87654321 + public_subnet = "50.97.46.160/28" + private_subnet = "10.56.109.128/26" + tags = [ + "collectd", + "mesos-master" + ] + raid = 5 + disk_key_names = [ "HARD_DRIVE_800GB_SSD", "HARD_DRIVE_800GB_SSD", "HARD_DRIVE_800GB_SSD" ] + redundant_power_supply = true +} +``` ## Argument Reference The following arguments are supported: @@ -171,3 +211,9 @@ The following attributes are exported: * `processor_type` - Processor type * `processors` - Number of processors. * `cores` - Number of cores. + +Raid configuration : https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID-through-API + +get array type ids : https://api.softlayer.com/rest/v3/SoftLayer_Configuration_Storage_Group_Array_Type/getAllObjects + +https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/1/getPartitionTemplates \ No newline at end of file diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 4974b2c3c..64bc62ceb 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -220,6 +220,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { }, // Custom bare metal server only + // Order single RAID group "raid": { Type: schema.TypeInt, Optional: true, @@ -255,6 +256,36 @@ func resourceSoftLayerBareMetal() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, DiffSuppressFunc: applyOnce, }, + + // Order multiple RAID groups + "storage_groups": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "array_type_id": { + Type: schema.TypeInt, + Required: true, + }, + "hard_drives": { + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeInt}, + Required: true, + }, + "array_size": { + Type: schema.TypeInt, + Optional: true, + }, + "partition_template_id": { + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + DiffSuppressFunc: applyOnce, + ConflictsWith: []string{"raid"}, + }, }, } } From 61bcf9a1ecab90b99e6b435e4d50e38103ca030d Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Fri, 7 Jul 2017 20:43:05 -0400 Subject: [PATCH 18/35] Added storageGroups logic to bare metal server. --- docs/resources/softlayer_bare_metal.md | 31 +++++++++++++++----- softlayer/resource_softlayer_bare_metal.go | 34 ++++++++++++++++------ 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index dfdd850bb..f6cec2563 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -14,10 +14,6 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { hourly_billing = true # Optional private_network_only = false # Optional user_metadata = "{\"value\":\"newvalue\"}" # Optional - public_vlan_id = 12345678 # Optional - private_vlan_id = 87654321 # Optional - public_subnet = "50.97.46.160/28" # Optional - private_subnet = "10.56.109.128/26" # Optional fixed_config_preset = "S1270_8GB_2X1TBSATA_NORAID" image_template_id = 12345 # Optional tags = [ @@ -31,25 +27,47 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { ```hcl # Create a new bare metal resource "softlayer_bare_metal" "quote_test" { + +# Mandatory attributes + hostname = "quote-bm-test" domain = "example.com" quote_id = 2209349 + +# Optional attributes + + user_metadata = "{\"value\":\"newvalue\"}" + public_vlan_id = 12345678 + private_vlan_id = 87654321 + public_subnet = "50.97.46.160/28" + private_subnet = "10.56.109.128/26" + tags = [ + "collectd", + "mesos-master" + ] + + } ``` # Example of custom bare metal server ```hcl resource "softlayer_bare_metal" "custom_bm1" { + +# Mandatory attributes package_key_name = "DUAL_E52600_V4_12_DRIVES" process_key_name = "INTEL_INTEL_XEON_E52620_V4_2_10" memory = 64 + os_reference_code = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" hostname = "cust-bm" domain = "ms.com" - os_reference_code = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" datacenter = "wdc04" network_speed = 100 public_bandwidth = 500 + disk_key_names = [ "HARD_DRIVE_800GB_SSD", "HARD_DRIVE_800GB_SSD", "HARD_DRIVE_800GB_SSD" ] hourly_billing = false + +# Optional attributes private_network_only = false unbonded_network = true user_metadata = "{\"value\":\"newvalue\"}" @@ -61,9 +79,8 @@ resource "softlayer_bare_metal" "custom_bm1" { "collectd", "mesos-master" ] - raid = 5 - disk_key_names = [ "HARD_DRIVE_800GB_SSD", "HARD_DRIVE_800GB_SSD", "HARD_DRIVE_800GB_SSD" ] redundant_power_supply = true + storage_groups } ``` ## Argument Reference diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 64bc62ceb..075dac136 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -219,14 +219,6 @@ func resourceSoftLayerBareMetal() *schema.Resource { Computed: true, }, - // Custom bare metal server only - // Order single RAID group - "raid": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - }, - // Custom bare metal server only "redundant_power_supply": { Type: schema.TypeBool, @@ -284,12 +276,31 @@ func resourceSoftLayerBareMetal() *schema.Resource { }, }, DiffSuppressFunc: applyOnce, - ConflictsWith: []string{"raid"}, }, }, } } +func prepareStorageGroups(d *schema.ResourceData) []datatypes.Container_Product_Order_Storage_Group { + storageGroupLists := d.Get("storage_groups").(*schema.Set).List() + storageGroups := make([]datatypes.Container_Product_Order_Storage_Group, len(storageGroupLists)) + + for _, storageGroupList := range storageGroupLists { + storageGroup := storageGroupList.(map[string]interface{}) + var storageGroupObj datatypes.Container_Product_Order_Storage_Group + storageGroupObj.ArrayTypeId = sl.Int(storageGroup["array_type_id"].(int)) + hardDrives := storageGroup["hard_drives"].([]interface{}) + storageGroupObj.HardDrives = make([]int, len(hardDrives)) + for _, hardDrive := range hardDrives { + storageGroupObj.HardDrives = append(storageGroupObj.HardDrives, hardDrive.(int)) + } + storageGroupObj.ArraySize = sl.Float(float64(storageGroup["array_size"].(int))) + storageGroupObj.PartitionTemplateId = sl.Int(storageGroup["partition_template_id"].(int)) + storageGroups = append(storageGroups, storageGroupObj) + } + return storageGroups +} + func getBareMetalOrderFromResourceData(d *schema.ResourceData, meta interface{}) (datatypes.Hardware, error) { dc := datatypes.Location{ Name: sl.String(d.Get("datacenter").(string)), @@ -904,6 +915,11 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype order.Prices = append(order.Prices, powerSupply) } + // Add storage_groups for RAID configuration + if _, ok := d.GetOk("storage_groups"); ok { + order.StorageGroups = prepareStorageGroups(d) + } + return order, nil } From ba3e21f36eaf0fd619acd9472167d98ad571567e Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Fri, 7 Jul 2017 21:17:41 -0400 Subject: [PATCH 19/35] Fixed disk_controller option for bare metal server. --- docs/resources/softlayer_bare_metal.md | 3 ++- softlayer/resource_softlayer_bare_metal.go | 19 +++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index f6cec2563..80716eeed 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -233,4 +233,5 @@ Raid configuration : https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID- get array type ids : https://api.softlayer.com/rest/v3/SoftLayer_Configuration_Storage_Group_Array_Type/getAllObjects -https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/1/getPartitionTemplates \ No newline at end of file +https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/1/getPartitionTemplates +https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/getAllObjects diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 075dac136..3990a7afb 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -283,14 +283,14 @@ func resourceSoftLayerBareMetal() *schema.Resource { func prepareStorageGroups(d *schema.ResourceData) []datatypes.Container_Product_Order_Storage_Group { storageGroupLists := d.Get("storage_groups").(*schema.Set).List() - storageGroups := make([]datatypes.Container_Product_Order_Storage_Group, len(storageGroupLists)) + storageGroups := make([]datatypes.Container_Product_Order_Storage_Group, 0) for _, storageGroupList := range storageGroupLists { storageGroup := storageGroupList.(map[string]interface{}) var storageGroupObj datatypes.Container_Product_Order_Storage_Group storageGroupObj.ArrayTypeId = sl.Int(storageGroup["array_type_id"].(int)) hardDrives := storageGroup["hard_drives"].([]interface{}) - storageGroupObj.HardDrives = make([]int, len(hardDrives)) + storageGroupObj.HardDrives = make([]int, 0) for _, hardDrive := range hardDrives { storageGroupObj.HardDrives = append(storageGroupObj.HardDrives, hardDrive.(int)) } @@ -873,16 +873,6 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype }, } - // Add disk controller - if raid, ok := d.GetOk("raid"); ok { - raidStr := "DISK_CONTROLLER_RAID_" + strconv.Itoa(raid.(int)) - diskController, err := getItemPriceId(items, "disk_controller", raidStr) - if err != nil { - return datatypes.Container_Product_Order{}, err - } - order.Prices = append(order.Prices, diskController) - } - // Add public bandwidth if publicBandwidth, ok := d.GetOk("public_bandwidth"); ok { publicBandwidthStr := "BANDWIDTH_" + strconv.Itoa(publicBandwidth.(int)) + "_GB" @@ -917,6 +907,11 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype // Add storage_groups for RAID configuration if _, ok := d.GetOk("storage_groups"); ok { + diskController, err := getItemPriceId(items, "disk_controller", "DISK_CONTROLLER_RAID") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + order.Prices = append(order.Prices, diskController) order.StorageGroups = prepareStorageGroups(d) } From 0f172e7b96fa1fc7676880934bacbff9bc038e8d Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Fri, 7 Jul 2017 23:13:34 -0400 Subject: [PATCH 20/35] Updated doc for bare metal server. --- docs/resources/softlayer_bare_metal.md | 37 ++++++++++++++++++---- softlayer/resource_softlayer_bare_metal.go | 34 ++++++++++++++++---- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index 80716eeed..1f2b1eea5 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -80,7 +80,15 @@ resource "softlayer_bare_metal" "custom_bm1" { "mesos-master" ] redundant_power_supply = true - storage_groups + storage_groups = { + # RAID 5 + array_type_id = 3 + # Use three disks + hard_drives = [ 0, 1, 2] + array_size = 1600 + # Basic partition template for windows + partition_template_id = 17 + } } ``` ## Argument Reference @@ -200,13 +208,31 @@ The following arguments are supported: * `memory` | *int* * Amount of memory(GB) for the server. * *Optional* -* `raid` | *int* - * RAID number for disks. +* `storage_groups` | *array of storage group objects* + * RAID and partition configuration. * *Optional* + + * Each storage group object has the following sub attributes: + * `array_type_id` | *int* + * It provides RAID type. You can find `array_type_id` from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Configuration_Storage_Group_Array_Type/getAllObjects). + * *Required* + * `hard_drives` | *array of int* + * Put the index of hard drives for RAID configuration. The index starts from 0. For example, if you want to use first two hard drives, you can use the following expression: [0,1] + * *Required* + * `array_size` | *int* + * Put target RAID disk size in GB unit. + * *Optional* + * `partition_template_id` | *int* + * Partition template id for OS disk. The templates are different based on the target OS. Check your OS with the [link](https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/getAllObjects ). Note the id of the OS and + check available partition templates using the link : https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/OS_ID/getPartitionTemplates . Replace `OS_ID` to your OS ID and choose your template id. + * *Optional* + * `redundant_power_supply` | *boolean* * If `redundant_power_supply` is true, additional power supply will be provided. * *Optional* - +* `tcp_monitoring` | *boolean* + * If `tcp_monitoring` is `false`, ping monitoring service will be provided. If `tcp_monitoring` is `true`, ping and tcp monitoring service will be provided. + * *Optional* **Quote based probisioning only attributes** * `quote_id` | *int* @@ -231,7 +257,4 @@ The following attributes are exported: Raid configuration : https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID-through-API -get array type ids : https://api.softlayer.com/rest/v3/SoftLayer_Configuration_Storage_Group_Array_Type/getAllObjects -https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/1/getPartitionTemplates -https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/getAllObjects diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 3990a7afb..cdd8f31e4 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -61,6 +61,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { "os_reference_code": { Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, ConflictsWith: []string{"image_template_id"}, }, @@ -277,6 +278,13 @@ func resourceSoftLayerBareMetal() *schema.Resource { }, DiffSuppressFunc: applyOnce, }, + + "tcp_monitoring": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Default: false, + }, }, } } @@ -294,8 +302,14 @@ func prepareStorageGroups(d *schema.ResourceData) []datatypes.Container_Product_ for _, hardDrive := range hardDrives { storageGroupObj.HardDrives = append(storageGroupObj.HardDrives, hardDrive.(int)) } - storageGroupObj.ArraySize = sl.Float(float64(storageGroup["array_size"].(int))) - storageGroupObj.PartitionTemplateId = sl.Int(storageGroup["partition_template_id"].(int)) + arraySize := storageGroup["array_size"].(int) + if arraySize > 0 { + storageGroupObj.ArraySize = sl.Float(float64(arraySize)) + } + partitionTemplateId := storageGroup["partition_template_id"].(int) + if partitionTemplateId > 0 { + storageGroupObj.PartitionTemplateId = sl.Int(partitionTemplateId) + } storageGroups = append(storageGroups, storageGroupObj) } return storageGroups @@ -819,6 +833,17 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } + monitoring, err := getItemPriceId(items, "monitoring", "MONITORING_HOST_PING") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + if d.Get("tcp_monitoring").(bool) { + monitoring, err = getItemPriceId(items, "monitoring", "MONITORING_HOST_PING_AND_TCP_SERVICE") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + } + // Other common default options priIpAddress, err := getItemPriceId(items, "pri_ip_addresses", "1_IP_ADDRESS") if err != nil { @@ -832,10 +857,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype if err != nil { return datatypes.Container_Product_Order{}, err } - monitoring, err := getItemPriceId(items, "monitoring", "MONITORING_HOST_PING") - if err != nil { - return datatypes.Container_Product_Order{}, err - } + notification, err := getItemPriceId(items, "notification", "NOTIFICATION_EMAIL_AND_TICKET") if err != nil { return datatypes.Container_Product_Order{}, err From 5eda78957cf41d9ca47fb15bdbde35335c6db630 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Sat, 8 Jul 2017 23:01:03 -0400 Subject: [PATCH 21/35] Updated doc for bare metal server. --- docs/resources/softlayer_bare_metal.md | 124 ++++++--- softlayer/resource_softlayer_bare_metal.go | 304 +++++++++++---------- 2 files changed, 252 insertions(+), 176 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index 1f2b1eea5..733f9e21e 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -1,8 +1,14 @@ # `softlayer_bare_metal` -Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. It provides three different ways to create bare metal servers. +Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. +`softlayer_bare_metal` resource supports both pre-set configured bare metal servers and custom bare metal servers. + For more detail, refer to the [link](https://www.ibm.com/cloud-computing/bluemix/bare-metal-servers) -# Example of Pre-set configured bare metal server +If the `softlayer_bare_metal` resource definition has an attribute `fixed_config_preset`, terraform creates pre-set configured +bare metal server. The following example creates a new pre-set configured bare metal server with hourly option. Except network speed, + other hardware specifications are already defined in the `fixed_config_preset` attribute. + +# Example of a pre-set configured bare metal server ```hcl # Create a new bare metal resource "softlayer_bare_metal" "pre-configured-bm1" { @@ -13,44 +19,60 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { network_speed = 100 # Optional hourly_billing = true # Optional private_network_only = false # Optional - user_metadata = "{\"value\":\"newvalue\"}" # Optional fixed_config_preset = "S1270_8GB_2X1TBSATA_NORAID" - image_template_id = 12345 # Optional - tags = [ - "collectd", - "mesos-master" - ] } ``` -# Example of quote based ordering +Users can use configure `user_metadata`, `tags`, and `notes` attributes as follows: + +# Example of addition attributes for the pre-set configured bare metal server ```hcl # Create a new bare metal -resource "softlayer_bare_metal" "quote_test" { - -# Mandatory attributes - - hostname = "quote-bm-test" +resource "softlayer_bare_metal" "pre-configured-bm1" { + hostname = "pre-configured-bm1" domain = "example.com" - quote_id = 2209349 - -# Optional attributes - - user_metadata = "{\"value\":\"newvalue\"}" - public_vlan_id = 12345678 - private_vlan_id = 87654321 - public_subnet = "50.97.46.160/28" - private_subnet = "10.56.109.128/26" + os_reference_code = "UBUNTU_16_64" + datacenter = "dal01" + network_speed = 100 # Optional + hourly_billing = true # Optional + private_network_only = false # Optional + fixed_config_preset = "S1270_8GB_2X1TBSATA_NORAID" + + user_metadata = "{\"value\":\"newvalue\"}" # Optional tags = [ "collectd", "mesos-master" ] + notes = "note test" +} +``` - +If the `fixed_config_preset` attribute is not configured, terraform consider it as a monthly custom bare metal server resource. It provides +options to configure process, memory, network, disk, and RAID. Users also can configure target VLANs and subnets. To configure the custom bare +metal server, you need to configure `package_key_name`, `proecss_key_name`, and `disk_key_names`. The folloing example descrices a basic configuration + of the custom bare metal server. + +# Example of a custom bare metal server +```hcl +resource "softlayer_bare_metal" "custom_bm1" { + package_key_name = "DUAL_E52600_V4_12_DRIVES" + process_key_name = "INTEL_INTEL_XEON_E52620_V4_2_10" + memory = 64 + os_reference_code = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" + hostname = "cust-bm" + domain = "ms.com" + datacenter = "wdc04" + network_speed = 100 + public_bandwidth = 500 + disk_key_names = [ "HARD_DRIVE_800GB_SSD", "HARD_DRIVE_800GB_SSD", "HARD_DRIVE_800GB_SSD" ] + hourly_billing = false } ``` -# Example of custom bare metal server +Users can configure many additional options. The following example configures target VLANs, subnets, and a RAID controller. `storage_groups` +configures RAIDs and disk partitioning. The [link](https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID-through-API) describes the RAID configuartion. + +# Example of a custom bare metal server with additional options. ```hcl resource "softlayer_bare_metal" "custom_bm1" { @@ -91,6 +113,48 @@ resource "softlayer_bare_metal" "custom_bm1" { } } ``` + +The most simplest way to create a bare metal server is using `quote_id` attribute. User can create a quote for specific bare metal servers. If + users already have a quote id for the bare metal server, they can create a new bare metal server with the quote id. You can find the quote id by + navigating the menu Account > Sales > Quotes on SoftLayer portal. The following example describes a basic configuration for a bare metal server with + quote_id. + +# Example of a quote based ordering +```hcl +# Create a new bare metal +resource "softlayer_bare_metal" "quote_test" { + hostname = "quote-bm-test" + domain = "example.com" + quote_id = 2209349 +} +``` + +Users can use additional options when they create a new bare metal server with `quote_id`. The folloing example defines target VLANs, subnets, + user meta data, and tags additionally. + +# Example of a quote based ordering with additional options +```hcl +# Create a new bare metal +resource "softlayer_bare_metal" "quote_test" { + +# Mandatory attributes + hostname = "quote-bm-test" + domain = "example.com" + quote_id = 2209349 + +# Optional attributes + user_metadata = "{\"value\":\"newvalue\"}" + public_vlan_id = 12345678 + private_vlan_id = 87654321 + public_subnet = "50.97.46.160/28" + private_subnet = "10.56.109.128/26" + tags = [ + "collectd", + "mesos-master" + ] +} +``` + ## Argument Reference The following arguments are supported: @@ -185,9 +249,11 @@ The following arguments are supported: * `package_key_name` | *string* * Custom bare metal server's package key name. This attribute is only used when a new custom bare metal server is created. + * You can find available key names in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}). You need your softlayer ID and api_key to access to the page. * *Optional* * `process_key_name` | *string* * Custom bare metal server's process key name. This attribute is only used when a new custom bare metal server is created. + * You can find available key names in the link: https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/PACKAGE_ID/getItems?objectMask=mask[prices[id,categories[id,name,categoryCode],capacityRestrictionType,capacityRestrictionMinimum,capacityRestrictionMaximum,locationGroupId]]&objectFilter= . Replace PACKAGE_ID to your package ID. The page also provides available `disk_key_names`. * *Optional* * `disk_key_names` | *list* * Array of internal disk key names. This attribute is only used when a new custom bare metal server is created. @@ -241,8 +307,6 @@ The following arguments are supported: * You can find the quote id by navigating on the portal to _Account > Sales > Quotes_, taking note of the id number in `QUOTE ID` column. * *Optional* - - ## Attributes Reference The following attributes are exported: @@ -250,11 +314,5 @@ The following attributes are exported: * `id` - id of the bare metal. * `public_ipv4_address` - Public IPv4 address of the bare metal server. * `private_ipv4_address` - Private IPv4 address of the bare metal server. -* `model` - Hardware model -* `processor_type` - Processor type -* `processors` - Number of processors. -* `cores` - Number of cores. - -Raid configuration : https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID-through-API diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index cdd8f31e4..6b408010b 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -58,87 +58,72 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, - "os_reference_code": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"image_template_id"}, - }, - - "hourly_billing": { - Type: schema.TypeBool, - Optional: true, - Default: true, - ForceNew: true, - }, - - "private_network_only": { - Type: schema.TypeBool, + "ssh_key_ids": { + Type: schema.TypeList, Optional: true, - Default: false, + Elem: &schema.Schema{Type: schema.TypeInt}, ForceNew: true, }, - // Custom bare metal server only - "redundant_network": { - Type: schema.TypeBool, + "user_metadata": { + Type: schema.TypeString, Optional: true, - Default: false, ForceNew: true, }, - // Custom bare metal server only - "unbonded_network": { - Type: schema.TypeBool, + "notes": { + Type: schema.TypeString, Optional: true, - Default: false, - ForceNew: true, }, - // Custom bare metal server only - "public_bandwidth": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, + "post_install_script_uri": { + Type: schema.TypeString, + Optional: true, + Default: nil, + ForceNew: true, + DiffSuppressFunc: applyOnce, }, - // Computed when a quote_id is povided. - "datacenter": { - Type: schema.TypeString, + "tags": { + Type: schema.TypeSet, Optional: true, - ForceNew: true, - Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, }, - "public_vlan_id": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - Computed: true, + // Pre-set configured bare metal server. - Mandatory attribute + "fixed_config_preset": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, }, - "public_subnet": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Computed: true, + // Pe-set configured / custom bare metal server - Mandatory attribute + "os_reference_code": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"image_template_id"}, }, - "private_vlan_id": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - Computed: true, + "image_template_id": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"os_reference_code"}, }, - "private_subnet": { + // Pre-set configured / custom bare metal server - Mandatory attribute + "datacenter": { Type: schema.TypeString, Optional: true, ForceNew: true, Computed: true, }, + // Pre-set configured / custom bare metal server "network_speed": { Type: schema.TypeInt, Optional: true, @@ -146,70 +131,75 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, - "public_ipv4_address": { - Type: schema.TypeString, - Computed: true, - }, - - "private_ipv4_address": { - Type: schema.TypeString, - Computed: true, - }, - - "ssh_key_ids": { - Type: schema.TypeList, + // Pre-set configured / custom bare metal server + "hourly_billing": { + Type: schema.TypeBool, Optional: true, - Elem: &schema.Schema{Type: schema.TypeInt}, + Default: true, ForceNew: true, }, - "user_metadata": { - Type: schema.TypeString, + // Pre-set configured / custom bare metal server + "private_network_only": { + Type: schema.TypeBool, Optional: true, + Default: false, ForceNew: true, }, - "notes": { - Type: schema.TypeString, + // Pre-set configured / custom bare metal server + "tcp_monitoring": { + Type: schema.TypeBool, Optional: true, + Default: false, }, - "post_install_script_uri": { + // Custom bare metal server - Mandatory attribute + "package_key_name": { Type: schema.TypeString, Optional: true, - Default: nil, ForceNew: true, DiffSuppressFunc: applyOnce, }, - // pre-configured bare metal server only - "fixed_config_preset": { + // Custom bare metal server - Mandatory attribute + "process_key_name": { Type: schema.TypeString, Optional: true, ForceNew: true, DiffSuppressFunc: applyOnce, }, - // Quote based provisioning only - "quote_id": { - Type: schema.TypeInt, + // Custom bare metal server - Mandatory attribute + "disk_key_names": { + Type: schema.TypeList, Optional: true, ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, DiffSuppressFunc: applyOnce, }, - "image_template_id": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"os_reference_code"}, + // Custom bare metal server only + "redundant_network": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, }, - "tags": { - Type: schema.TypeSet, + // Custom bare metal server only + "unbonded_network": { + Type: schema.TypeBool, Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, + Default: false, + ForceNew: true, + }, + + // Custom bare metal server only + "public_bandwidth": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, }, // Custom bare metal server only @@ -227,30 +217,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { Computed: true, }, - // Custom bare metal server requires key names for baremetal package, process, and disks. - "package_key_name": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - DiffSuppressFunc: applyOnce, - }, - - "process_key_name": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - DiffSuppressFunc: applyOnce, - }, - - "disk_key_names": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, - DiffSuppressFunc: applyOnce, - }, - - // Order multiple RAID groups + // Custom bare metal server only - Order multiple RAID groups "storage_groups": { Type: schema.TypeSet, Optional: true, @@ -279,40 +246,57 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, - "tcp_monitoring": { - Type: schema.TypeBool, + // Quote based provisioning only + "quote_id": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, + }, + + // Quote based ordering/custom bare metal server only + "public_vlan_id": { + Type: schema.TypeInt, Optional: true, + ForceNew: true, Computed: true, - Default: false, }, - }, - } -} -func prepareStorageGroups(d *schema.ResourceData) []datatypes.Container_Product_Order_Storage_Group { - storageGroupLists := d.Get("storage_groups").(*schema.Set).List() - storageGroups := make([]datatypes.Container_Product_Order_Storage_Group, 0) + // Quote based ordering/custom bare metal server only + "public_subnet": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, - for _, storageGroupList := range storageGroupLists { - storageGroup := storageGroupList.(map[string]interface{}) - var storageGroupObj datatypes.Container_Product_Order_Storage_Group - storageGroupObj.ArrayTypeId = sl.Int(storageGroup["array_type_id"].(int)) - hardDrives := storageGroup["hard_drives"].([]interface{}) - storageGroupObj.HardDrives = make([]int, 0) - for _, hardDrive := range hardDrives { - storageGroupObj.HardDrives = append(storageGroupObj.HardDrives, hardDrive.(int)) - } - arraySize := storageGroup["array_size"].(int) - if arraySize > 0 { - storageGroupObj.ArraySize = sl.Float(float64(arraySize)) - } - partitionTemplateId := storageGroup["partition_template_id"].(int) - if partitionTemplateId > 0 { - storageGroupObj.PartitionTemplateId = sl.Int(partitionTemplateId) - } - storageGroups = append(storageGroups, storageGroupObj) + // Quote based ordering/custom bare metal server only + "private_vlan_id": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Computed: true, + }, + + // Quote based ordering/custom bare metal server only + "private_subnet": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + }, + + "public_ipv4_address": { + Type: schema.TypeString, + Computed: true, + }, + + "private_ipv4_address": { + Type: schema.TypeString, + Computed: true, + }, + }, } - return storageGroups } func getBareMetalOrderFromResourceData(d *schema.ResourceData, meta interface{}) (datatypes.Hardware, error) { @@ -440,7 +424,7 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) } } - order, err = setCommonBareMetalOptions(d, meta, order) + order, err = setCommonBareMetalOrderOptions(d, meta, order) if err != nil { return fmt.Errorf( "Encountered problem trying to configure bare metal server options: %s", err) @@ -749,7 +733,8 @@ func setHardwareNotes(id int, d *schema.ResourceData, meta interface{}) error { return nil } -// Example : getItemPriceId(items, 'server', 'INTEL_XEON_2690_2_60') +// Returns a price from an item list. +// Example usage : getItemPriceId(items, 'server', 'INTEL_XEON_2690_2_60') func getItemPriceId(items []datatypes.Product_Item, categoryCode string, keyName string) (datatypes.Product_Item_Price, error) { availableItems := "" for _, item := range items { @@ -777,7 +762,6 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, fmt.Errorf("Custom bare metal server only supports monthly billing.") } - // Check mandatory attributes of custom bare metal server ordering. model, ok := d.GetOk("package_key_name") if !ok { return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'package_key_name' is not defined.") @@ -798,6 +782,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } + // 1. Find a package id using custom bare metal package key name. pkg, err := getPackageByModel(sess, model.(string)) if err != nil { return datatypes.Container_Product_Order{}, err @@ -871,6 +856,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } + // Define an order object using basic paramters. order := datatypes.Container_Product_Order{ Quantity: sl.Int(1), Hardware: []datatypes.Hardware{{ @@ -895,6 +881,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype }, } + // Add optional price ids. // Add public bandwidth if publicBandwidth, ok := d.GetOk("public_bandwidth"); ok { publicBandwidthStr := "BANDWIDTH_" + strconv.Itoa(publicBandwidth.(int)) + "_GB" @@ -934,13 +921,14 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } order.Prices = append(order.Prices, diskController) - order.StorageGroups = prepareStorageGroups(d) + order.StorageGroups = getStorageGroupsFromResourceData(d) } return order, nil } -func setCommonBareMetalOptions(d *schema.ResourceData, meta interface{}, order datatypes.Container_Product_Order) (datatypes.Container_Product_Order, error) { +// Set common parameters for quite based, pre-set configured, and custom bare metal server ordering. +func setCommonBareMetalOrderOptions(d *schema.ResourceData, meta interface{}, order datatypes.Container_Product_Order) (datatypes.Container_Product_Order, error) { public_vlan_id := d.Get("public_vlan_id").(int) if public_vlan_id > 0 { @@ -1002,6 +990,7 @@ func setCommonBareMetalOptions(d *schema.ResourceData, meta interface{}, order d return order, nil } +// Find price item using network options func findNetworkItemPriceId(items []datatypes.Product_Item, d *schema.ResourceData) (datatypes.Product_Item_Price, error) { networkSpeed := d.Get("network_speed").(int) redundantNetwork := d.Get("redundant_network").(bool) @@ -1050,6 +1039,7 @@ func findNetworkItemPriceId(items []datatypes.Product_Item, d *schema.ResourceDa networkSpeedStr, redundantNetworkStr, unbondedNetworkStr, privateNetworkOnly) } +// Find memory price item using memory size. func findMemoryItemPriceId(items []datatypes.Product_Item, d *schema.ResourceData) (datatypes.Product_Item_Price, error) { memory := d.Get("memory").(int) memoryStr := "RAM_" + strconv.Itoa(memory) + "_GB" @@ -1074,6 +1064,7 @@ func findMemoryItemPriceId(items []datatypes.Product_Item, d *schema.ResourceDat fmt.Errorf("Could not find the price item for %d GB memory. Available items are %s", memory, availableMemories) } +// Find a bare metal package object using a package key name func getPackageByModel(sess *session.Session, model string) (datatypes.Product_Package, error) { objectMask := "id,keyName,name,description,isActive,type[keyName]" service := services.GetProductPackageService(sess) @@ -1105,6 +1096,33 @@ func getPackageByModel(sess *session.Session, model string) (datatypes.Product_P return datatypes.Product_Package{}, fmt.Errorf("No custom bare metal package key name for %s. Available package key name(s) is(are) %s", model, availableModels) } +func getStorageGroupsFromResourceData(d *schema.ResourceData) []datatypes.Container_Product_Order_Storage_Group { + storageGroupLists := d.Get("storage_groups").(*schema.Set).List() + storageGroups := make([]datatypes.Container_Product_Order_Storage_Group, 0) + + for _, storageGroupList := range storageGroupLists { + storageGroup := storageGroupList.(map[string]interface{}) + var storageGroupObj datatypes.Container_Product_Order_Storage_Group + storageGroupObj.ArrayTypeId = sl.Int(storageGroup["array_type_id"].(int)) + hardDrives := storageGroup["hard_drives"].([]interface{}) + storageGroupObj.HardDrives = make([]int, 0) + for _, hardDrive := range hardDrives { + storageGroupObj.HardDrives = append(storageGroupObj.HardDrives, hardDrive.(int)) + } + arraySize := storageGroup["array_size"].(int) + if arraySize > 0 { + storageGroupObj.ArraySize = sl.Float(float64(arraySize)) + } + partitionTemplateId := storageGroup["partition_template_id"].(int) + if partitionTemplateId > 0 { + storageGroupObj.PartitionTemplateId = sl.Int(partitionTemplateId) + } + storageGroups = append(storageGroups, storageGroupObj) + } + return storageGroups +} + +// Use this function for attributes which only should be applied in resource creation time. func applyOnce(k, o, n string, d *schema.ResourceData) bool { if len(d.Id()) == 0 { return false From 6c72492271912ea89d8ef097b2482058097de1a9 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Sun, 9 Jul 2017 22:37:43 -0400 Subject: [PATCH 22/35] Updated the doc for bare metal server. --- docs/resources/softlayer_bare_metal.md | 68 ++++++++++++-------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index 733f9e21e..ac0e80c34 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -2,13 +2,14 @@ Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. `softlayer_bare_metal` resource supports both pre-set configured bare metal servers and custom bare metal servers. - For more detail, refer to the [link](https://www.ibm.com/cloud-computing/bluemix/bare-metal-servers) + For more detail of bare metal types, refer to the [link](https://www.ibm.com/cloud-computing/bluemix/bare-metal-servers) -If the `softlayer_bare_metal` resource definition has an attribute `fixed_config_preset`, terraform creates pre-set configured -bare metal server. The following example creates a new pre-set configured bare metal server with hourly option. Except network speed, - other hardware specifications are already defined in the `fixed_config_preset` attribute. +## Pre-set configured bare metal server +If the `softlayer_bare_metal` resource definition has a `fixed_config_preset` attribute, terraform will create a pre-set configured +bare metal server. The following example creates a new pre-set configured bare metal server with hourly option. Hardware specifications +are already defined in the `fixed_config_preset` attribute and cannot be modified. -# Example of a pre-set configured bare metal server +## Example of a pre-set configured bare metal server ```hcl # Create a new bare metal resource "softlayer_bare_metal" "pre-configured-bm1" { @@ -23,9 +24,9 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { } ``` -Users can use configure `user_metadata`, `tags`, and `notes` attributes as follows: +In addition, users can use configure optional attributes such as `user_metadata`, `tags`, and `notes` attributes as follows: -# Example of addition attributes for the pre-set configured bare metal server +## Example of additional attributes for the pre-set configured bare metal server ```hcl # Create a new bare metal resource "softlayer_bare_metal" "pre-configured-bm1" { @@ -47,12 +48,13 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { } ``` -If the `fixed_config_preset` attribute is not configured, terraform consider it as a monthly custom bare metal server resource. It provides -options to configure process, memory, network, disk, and RAID. Users also can configure target VLANs and subnets. To configure the custom bare +## Custom bare metal server +If the `fixed_config_preset` attribute is not configured, terraform will consider it as a monthly custom bare metal server resource. It provides +options to configure process, memory, network, disk, and RAID. Users also can assign VLANs and subnets for the target custom bare metal server. To configure the custom bare metal server, you need to configure `package_key_name`, `proecss_key_name`, and `disk_key_names`. The folloing example descrices a basic configuration of the custom bare metal server. -# Example of a custom bare metal server +## Example of a custom bare metal server ```hcl resource "softlayer_bare_metal" "custom_bm1" { package_key_name = "DUAL_E52600_V4_12_DRIVES" @@ -69,10 +71,10 @@ resource "softlayer_bare_metal" "custom_bm1" { } ``` -Users can configure many additional options. The following example configures target VLANs, subnets, and a RAID controller. `storage_groups` -configures RAIDs and disk partitioning. The [link](https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID-through-API) describes the RAID configuartion. +Users can configure additional options. The following example configures target VLANs, subnets, and a RAID controller. `storage_groups` +configures RAIDs and disk partitioning. Refer to the [link](https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID-through-API) to configure `storage_groups`. -# Example of a custom bare metal server with additional options. +## Example of a custom bare metal server with additional options ```hcl resource "softlayer_bare_metal" "custom_bm1" { @@ -114,12 +116,11 @@ resource "softlayer_bare_metal" "custom_bm1" { } ``` -The most simplest way to create a bare metal server is using `quote_id` attribute. User can create a quote for specific bare metal servers. If - users already have a quote id for the bare metal server, they can create a new bare metal server with the quote id. You can find the quote id by - navigating the menu Account > Sales > Quotes on SoftLayer portal. The following example describes a basic configuration for a bare metal server with +## Create a bare metal server using quote ID +If users already have a quote id for the bare metal server, they can create a new bare metal server with the quote id. The following example describes a basic configuration for a bare metal server with quote_id. -# Example of a quote based ordering +## Example of a quote based ordering ```hcl # Create a new bare metal resource "softlayer_bare_metal" "quote_test" { @@ -132,7 +133,7 @@ resource "softlayer_bare_metal" "quote_test" { Users can use additional options when they create a new bare metal server with `quote_id`. The folloing example defines target VLANs, subnets, user meta data, and tags additionally. -# Example of a quote based ordering with additional options +## Example of a quote based ordering with additional options ```hcl # Create a new bare metal resource "softlayer_bare_metal" "quote_test" { @@ -182,7 +183,7 @@ The following arguments are supported: * `post_install_script_uri` | *string* * As defined in the [SoftLayer_Virtual_Guest_SupplementalCreateObjectOptions](https://sldn.softlayer.com/reference/datatypes/SoftLayer_Virtual_Guest_SupplementalCreateObjectOptions). * *Optional* -* `tags` | *array* of strings +* `tags` | *array* of strings * Set tags on this bare metal server. The characters permitted are A-Z, 0-9, whitespace, _ (underscore), - (hyphen), . (period), and : (colon). All other characters will be stripped away. * *Optional* @@ -199,8 +200,8 @@ The following arguments are supported: * *Optional* * `os_reference_code` | *string* * An operating system reference code that will be used to provision the computing instance. - * [Get a complete list of the os reference codes available for pre-set configuration bare metal servers](https://api.softlayer.com/rest/v3/SoftLayer_Virtual_Guest_Block_Device_Template_Group/getVhdImportSoftwareDescriptions.json?objectMask=referenceCode) (use your api key as the password). - * [Get a complete list of the os reference codes available for custom bare metal servers]() (use your api key as the password). + * Pre-set configured bare metal server : [Get a complete list of the os reference codes available for pre-set configuration bare metal servers](https://api.softlayer.com/rest/v3/SoftLayer_Virtual_Guest_Block_Device_Template_Group/getVhdImportSoftwareDescriptions.json?objectMask=referenceCode) (use your api key as the password). + * Custom bare metal server : Note the package key ID from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}) and replace **PACKAGE_ID** in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/PACKAGE_ID/getItems?objectMask=mask[prices[id,categories[id,name,categoryCode],capacityRestrictionType,capacityRestrictionMinimum,capacityRestrictionMaximum,locationGroupId]]) to your package key ID. Select a OS key name from available OS key names. * *Optional* * **Conflicts with** `image_template_id`. * `image_template_id` | *int* @@ -230,33 +231,30 @@ The following arguments are supported: * `public_vlan_id` | *int* * Public VLAN which is to be used for the public network interface of the instance. Accepted values can be found [here](https://control.softlayer.com/network/vlans). Click on the desired VLAN and note the id number in the URL. - * Only custom bare metal servers support this attribute. * *Optional* * `private_vlan_id` | *int* * Private VLAN which is to be used for the private network interface of the instance. Accepted values can be found [here](https://control.softlayer.com/network/vlans). Click on the desired VLAN and note the id number in the URL. - * Only custom bare metal servers support this attribute. * *Optional* * `public_subnet` | *string* * Public subnet which is to be used for the public network interface of the instance. Accepted values are primary public networks and can be found [here](https://control.softlayer.com/network/subnets). - * Only custom bare metal servers support this attribute. * *Optional* * `private_subnet` | *string* * Private subnet which is to be used for the private network interface of the instance. Accepted values are primary private networks and can be found [here](https://control.softlayer.com/network/subnets). - * Only custom bare metal servers support this attribute. * *Optional* **Custom bare metal server only attributes** * `package_key_name` | *string* * Custom bare metal server's package key name. This attribute is only used when a new custom bare metal server is created. - * You can find available key names in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}). You need your softlayer ID and api_key to access to the page. + * You can find available key names in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}). You need your username and api_key to access to the page. * *Optional* * `process_key_name` | *string* * Custom bare metal server's process key name. This attribute is only used when a new custom bare metal server is created. - * You can find available key names in the link: https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/PACKAGE_ID/getItems?objectMask=mask[prices[id,categories[id,name,categoryCode],capacityRestrictionType,capacityRestrictionMinimum,capacityRestrictionMaximum,locationGroupId]]&objectFilter= . Replace PACKAGE_ID to your package ID. The page also provides available `disk_key_names`. + * Note the package key ID from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}) and replace **PACKAGE_ID** in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/PACKAGE_ID/getItems?objectMask=mask[prices[id,categories[id,name,categoryCode],capacityRestrictionType,capacityRestrictionMinimum,capacityRestrictionMaximum,locationGroupId]]) to your package key ID. Select a process key name from available process key names. * *Optional* * `disk_key_names` | *list* * Array of internal disk key names. This attribute is only used when a new custom bare metal server is created. + * Note the package key ID from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}) and replace **PACKAGE_ID** in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/PACKAGE_ID/getItems?objectMask=mask[prices[id,categories[id,name,categoryCode],capacityRestrictionType,capacityRestrictionMinimum,capacityRestrictionMaximum,locationGroupId]]) to your package key ID. Select a disk key name from available disk key names. * *Optional* * `redundant_network` | *boolean* * If `redundant_network` is `true`, two physical network interfaces will be provided with a bonding configuration. @@ -264,11 +262,10 @@ The following arguments are supported: * *Optional* * `unbonded_network` | *boolean* * If `unbonded_network` is `true`, two physical network interfaces will be provided. - * unbonded_network cannot be `true` when redudant_network is `true`. * *Default*: False * *Optional* * `public_bandwidth` | *int* - * Public network traffic(GB) per month which can be used without additional charge. + * Allowed public network traffic(GB) per month. * `public_bandwidth` can be greater than 0 when `private_network_only` is `false` and the server is a monthly based server. * *Optional* * `memory` | *int* @@ -277,20 +274,19 @@ The following arguments are supported: * `storage_groups` | *array of storage group objects* * RAID and partition configuration. * *Optional* - * Each storage group object has the following sub attributes: * `array_type_id` | *int* * It provides RAID type. You can find `array_type_id` from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Configuration_Storage_Group_Array_Type/getAllObjects). * *Required* * `hard_drives` | *array of int* - * Put the index of hard drives for RAID configuration. The index starts from 0. For example, if you want to use first two hard drives, you can use the following expression: [0,1] + * Index of hard drives for RAID configuration. The index starts from 0. For example, if you want to use first two hard drives, you will use the following expression: [0,1] * *Required* * `array_size` | *int* - * Put target RAID disk size in GB unit. + * Target RAID disk size in GB unit. * *Optional* * `partition_template_id` | *int* * Partition template id for OS disk. The templates are different based on the target OS. Check your OS with the [link](https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/getAllObjects ). Note the id of the OS and - check available partition templates using the link : https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/OS_ID/getPartitionTemplates . Replace `OS_ID` to your OS ID and choose your template id. + check available partition templates using the URL : https://api.softlayer.com/rest/v3/SoftLayer_Hardware_Component_Partition_OperatingSystem/OS_ID/getPartitionTemplates . Replace `OS_ID` to your OS ID from the URL and find your template id. * *Optional* * `redundant_power_supply` | *boolean* @@ -304,7 +300,7 @@ The following arguments are supported: * `quote_id` | *int* * Create a pre-set configured bare metal server or custom bare metal server using the quote. * If quote_id is defined, the terraform uses specifications in the quote to create a bare metal server. - * You can find the quote id by navigating on the portal to _Account > Sales > Quotes_, taking note of the id number in `QUOTE ID` column. + * You can find the quote id by navigating on the portal to _Account > Sales > Quotes_ . * *Optional* ## Attributes Reference @@ -313,6 +309,4 @@ The following attributes are exported: * `id` - id of the bare metal. * `public_ipv4_address` - Public IPv4 address of the bare metal server. -* `private_ipv4_address` - Private IPv4 address of the bare metal server. - - +* `private_ipv4_address` - Private IPv4 address of the bare metal server. \ No newline at end of file From d97313f1bd8933207d85816af8a7b98b0289ce53 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Sun, 9 Jul 2017 22:47:12 -0400 Subject: [PATCH 23/35] Updated doc for bare metal server. --- docs/resources/softlayer_bare_metal.md | 28 ++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index ac0e80c34..8f8635c2d 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -1,15 +1,14 @@ # `softlayer_bare_metal` -Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. -`softlayer_bare_metal` resource supports both pre-set configured bare metal servers and custom bare metal servers. - For more detail of bare metal types, refer to the [link](https://www.ibm.com/cloud-computing/bluemix/bare-metal-servers) +Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. `softlayer_bare_metal` resource supports both pre-set configured bare metal servers and custom bare metal servers. + For more detail on bare metal types, refer to the [link](https://www.ibm.com/cloud-computing/bluemix/bare-metal-servers) ## Pre-set configured bare metal server If the `softlayer_bare_metal` resource definition has a `fixed_config_preset` attribute, terraform will create a pre-set configured -bare metal server. The following example creates a new pre-set configured bare metal server with hourly option. Hardware specifications +bare metal server. The following example creates a new pre-set configured bare metal server with an hourly option. Hardware specifications are already defined in the `fixed_config_preset` attribute and cannot be modified. -## Example of a pre-set configured bare metal server +### Example of a pre-set configured bare metal server ```hcl # Create a new bare metal resource "softlayer_bare_metal" "pre-configured-bm1" { @@ -26,7 +25,7 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { In addition, users can use configure optional attributes such as `user_metadata`, `tags`, and `notes` attributes as follows: -## Example of additional attributes for the pre-set configured bare metal server +### Example of additional attributes for the pre-set configured bare metal server ```hcl # Create a new bare metal resource "softlayer_bare_metal" "pre-configured-bm1" { @@ -49,12 +48,12 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { ``` ## Custom bare metal server -If the `fixed_config_preset` attribute is not configured, terraform will consider it as a monthly custom bare metal server resource. It provides +If the `fixed_config_preset` attribute is not configured, terraform will consider it as a monthly custom bare metal server resource. It provides options to configure process, memory, network, disk, and RAID. Users also can assign VLANs and subnets for the target custom bare metal server. To configure the custom bare -metal server, you need to configure `package_key_name`, `proecss_key_name`, and `disk_key_names`. The folloing example descrices a basic configuration +metal server, you need to configure `package_key_name`, `proecss_key_name`, and `disk_key_names`. The following example describes a basic configuration of the custom bare metal server. -## Example of a custom bare metal server +### Example of a custom bare metal server ```hcl resource "softlayer_bare_metal" "custom_bm1" { package_key_name = "DUAL_E52600_V4_12_DRIVES" @@ -72,9 +71,8 @@ resource "softlayer_bare_metal" "custom_bm1" { ``` Users can configure additional options. The following example configures target VLANs, subnets, and a RAID controller. `storage_groups` -configures RAIDs and disk partitioning. Refer to the [link](https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID-through-API) to configure `storage_groups`. - -## Example of a custom bare metal server with additional options +configures RAIDs and disk partitioning. +### Example of a custom bare metal server with additional options ```hcl resource "softlayer_bare_metal" "custom_bm1" { @@ -120,7 +118,7 @@ resource "softlayer_bare_metal" "custom_bm1" { If users already have a quote id for the bare metal server, they can create a new bare metal server with the quote id. The following example describes a basic configuration for a bare metal server with quote_id. -## Example of a quote based ordering +### Example of a quote based ordering ```hcl # Create a new bare metal resource "softlayer_bare_metal" "quote_test" { @@ -133,7 +131,7 @@ resource "softlayer_bare_metal" "quote_test" { Users can use additional options when they create a new bare metal server with `quote_id`. The folloing example defines target VLANs, subnets, user meta data, and tags additionally. -## Example of a quote based ordering with additional options +### Example of a quote based ordering with additional options ```hcl # Create a new bare metal resource "softlayer_bare_metal" "quote_test" { @@ -272,7 +270,7 @@ The following arguments are supported: * Amount of memory(GB) for the server. * *Optional* * `storage_groups` | *array of storage group objects* - * RAID and partition configuration. + * RAID and partition configuration. Refer to the [link](https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID-through-API) to configure `storage_groups`. * *Optional* * Each storage group object has the following sub attributes: * `array_type_id` | *int* From f31e851d630fe96dc36eea12752031d367f867ad Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Sun, 9 Jul 2017 22:50:11 -0400 Subject: [PATCH 24/35] Delete resource_softlayer_bare_metal_quote and quote_test. --- .../resource_softlayer_bare_metal_quote.go | 397 ------------------ ...esource_softlayer_bare_metal_quote_test.go | 138 ------ 2 files changed, 535 deletions(-) delete mode 100644 softlayer/resource_softlayer_bare_metal_quote.go delete mode 100644 softlayer/resource_softlayer_bare_metal_quote_test.go diff --git a/softlayer/resource_softlayer_bare_metal_quote.go b/softlayer/resource_softlayer_bare_metal_quote.go deleted file mode 100644 index cd46af34e..000000000 --- a/softlayer/resource_softlayer_bare_metal_quote.go +++ /dev/null @@ -1,397 +0,0 @@ -package softlayer - -import ( - "fmt" - "log" - "strconv" - "strings" - "time" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/helper/schema" - "github.com/softlayer/softlayer-go/datatypes" - "github.com/softlayer/softlayer-go/filter" - "github.com/softlayer/softlayer-go/services" - "github.com/softlayer/softlayer-go/sl" -) - -func resourceSoftLayerBareMetalQuote() *schema.Resource { - return &schema.Resource{ - Create: resourceSoftLayerBareMetalQuoteCreate, - Read: resourceSoftLayerBareMetalQuoteRead, - Update: resourceSoftLayerBareMetalQuoteUpdate, - Delete: resourceSoftLayerBareMetalQuoteDelete, - Exists: resourceSoftLayerBareMetalQuoteExists, - Importer: &schema.ResourceImporter{}, - - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeInt, - Computed: true, - }, - - "hostname": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - DefaultFunc: genId, - DiffSuppressFunc: func(k, o, n string, d *schema.ResourceData) bool { - // FIXME: Work around another bug in terraform. - // When a default function is used with an optional property, - // terraform will always execute it on apply, even when the property - // already has a value in the state for it. This causes a false diff. - // Making the property Computed:true does not make a difference. - if strings.HasPrefix(o, "terraformed-") && strings.HasPrefix(n, "terraformed-") { - return true - } - - return o == n - }, - }, - - "domain": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "public_vlan_id": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - Computed: true, - }, - - "public_subnet": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Computed: true, - }, - - "private_vlan_id": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - Computed: true, - }, - - "private_subnet": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Computed: true, - }, - - "public_ipv4_address": { - Type: schema.TypeString, - Computed: true, - }, - - "private_ipv4_address": { - Type: schema.TypeString, - Computed: true, - }, - - "ssh_key_ids": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeInt}, - ForceNew: true, - }, - - "user_metadata": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - - "notes": { - Type: schema.TypeString, - Optional: true, - }, - - "post_install_script_uri": { - Type: schema.TypeString, - Optional: true, - Default: nil, - ForceNew: true, - }, - - "quote_id": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - - "tags": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, - }, - } -} - -func resourceSoftLayerBareMetalQuoteCreate(d *schema.ResourceData, meta interface{}) error { - sess := meta.(ProviderConfig).SoftLayerSession() - orderService := services.GetProductOrderService(sess) - quoteService := services.GetBillingOrderQuoteService(sess) - - order, err := quoteService.Id(d.Get("quote_id").(int)).GetRecalculatedOrderContainer(nil, sl.Bool(false)) - if err != nil { - return fmt.Errorf( - "Encountered problem trying to get the bare metal order template from quote: %s", err) - } - - // Set additional parameters - order.Quantity = sl.Int(1) - order.PresetId = nil - order.Hardware = make([]datatypes.Hardware, 0, 1) - order.Hardware = append( - order.Hardware, - datatypes.Hardware{ - Hostname: sl.String(d.Get("hostname").(string)), - Domain: sl.String(d.Get("domain").(string)), - }, - ) - hardware := datatypes.Hardware{ - Hostname: sl.String(d.Get("hostname").(string)), - Domain: sl.String(d.Get("domain").(string)), - } - - log.Println("[INFO] Ordering bare metal server") - - _, err = orderService.PlaceOrder(&order, sl.Bool(false)) - if err != nil { - return fmt.Errorf("Error ordering bare metal server: %s", err) - } - - log.Printf("[INFO] Bare Metal Server ID: %s", d.Id()) - - // wait for machine availability - bm, err := waitForBareMetalProvision(&hardware, meta) - if err != nil { - return fmt.Errorf( - "Error waiting for bare metal server (%s) to become ready: %s", d.Id(), err) - } - - id := *bm.(datatypes.Hardware).Id - d.SetId(fmt.Sprintf("%d", id)) - - // Set tags - err = setHardwareTags(id, d, meta) - if err != nil { - return err - } - - // Set notes - if d.Get("notes").(string) != "" { - err = setHardwareNotes(id, d, meta) - if err != nil { - return err - } - } - - return resourceSoftLayerBareMetalRead(d, meta) -} - -func resourceSoftLayerBareMetalQuoteRead(d *schema.ResourceData, meta interface{}) error { - service := services.GetHardwareService(meta.(ProviderConfig).SoftLayerSession()) - - id, err := strconv.Atoi(d.Id()) - if err != nil { - return fmt.Errorf("Not a valid ID, must be an integer: %s", err) - } - - result, err := service.Id(id).Mask( - "hostname,domain," + - "primaryIpAddress,primaryBackendIpAddress,privateNetworkOnlyFlag," + - "notes,userData[value],tagReferences[id,tag[name]]," + - "hourlyBillingFlag," + - "datacenter[id,name,longName]," + - "primaryNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed]," + - "primaryBackendNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed]", - ).GetObject() - - if err != nil { - return fmt.Errorf("Error retrieving bare metal server: %s", err) - } - - d.Set("hostname", *result.Hostname) - d.Set("domain", *result.Domain) - - if result.PrimaryIpAddress != nil { - d.Set("public_ipv4_address", *result.PrimaryIpAddress) - } - d.Set("private_ipv4_address", *result.PrimaryBackendIpAddress) - - if result.PrimaryNetworkComponent.NetworkVlan != nil { - d.Set("public_vlan_id", *result.PrimaryNetworkComponent.NetworkVlan.Id) - } - - if result.PrimaryBackendNetworkComponent.NetworkVlan != nil { - d.Set("private_vlan_id", *result.PrimaryBackendNetworkComponent.NetworkVlan.Id) - } - - userData := result.UserData - if len(userData) > 0 && userData[0].Value != nil { - d.Set("user_metadata", *userData[0].Value) - } - - d.Set("notes", sl.Get(result.Notes, nil)) - - tagReferences := result.TagReferences - tagReferencesLen := len(tagReferences) - if tagReferencesLen > 0 { - tags := make([]string, 0, tagReferencesLen) - for _, tagRef := range tagReferences { - tags = append(tags, *tagRef.Tag.Name) - } - d.Set("tags", tags) - } - - connInfo := map[string]string{"type": "ssh"} - connInfo["host"] = *result.PrimaryBackendIpAddress - d.SetConnInfo(connInfo) - - return nil -} - -func resourceSoftLayerBareMetalQuoteUpdate(d *schema.ResourceData, meta interface{}) error { - id, _ := strconv.Atoi(d.Id()) - - if d.HasChange("tags") { - err := setHardwareTags(id, d, meta) - if err != nil { - return err - } - } - - if d.HasChange("notes") { - err := setHardwareNotes(id, d, meta) - if err != nil { - return err - } - } - - return nil -} - -func resourceSoftLayerBareMetalQuoteDelete(d *schema.ResourceData, meta interface{}) error { - sess := meta.(ProviderConfig).SoftLayerSession() - service := services.GetHardwareService(sess) - - id, err := strconv.Atoi(d.Id()) - if err != nil { - return fmt.Errorf("Not a valid ID, must be an integer: %s", err) - } - - _, err = waitForNoBareMetalActiveTransactions(id, meta) - if err != nil { - return fmt.Errorf("Error deleting bare metal server while waiting for zero active transactions: %s", err) - } - - billingItem, err := service.Id(id).GetBillingItem() - if err != nil { - return fmt.Errorf("Error getting billing item for bare metal server: %s", err) - } - - billingItemService := services.GetBillingItemService(sess) - _, err = billingItemService.Id(*billingItem.Id).CancelItem( - sl.Bool(false), sl.Bool(true), sl.String("No longer required"), sl.String("Please cancel this server"), - ) - if err != nil { - return fmt.Errorf("Error canceling the bare metal server (%d): %s", id, err) - } - - return nil -} - -func resourceSoftLayerBareMetalQuoteExists(d *schema.ResourceData, meta interface{}) (bool, error) { - service := services.GetHardwareService(meta.(ProviderConfig).SoftLayerSession()) - - id, err := strconv.Atoi(d.Id()) - if err != nil { - return false, fmt.Errorf("Not a valid ID, must be an integer: %s", err) - } - - result, err := service.Id(id).GetObject() - if err != nil { - if apiErr, ok := err.(sl.Error); !ok || apiErr.StatusCode != 404 { - return false, fmt.Errorf("Error trying to retrieve the Bare Metal server: %s", err) - } - } - - return err == nil && result.Id != nil && *result.Id == id, nil -} - -// Bare metal creation does not return a bare metal object with an Id. -// Have to wait on provision date to become available on server that matches -// hostname and domain. -// http://sldn.softlayer.com/blog/bpotter/ordering-bare-metal-servers-using-softlayer-api -func waitForBareMetalQuoteProvision(d *datatypes.Hardware, meta interface{}) (interface{}, error) { - hostname := *d.Hostname - domain := *d.Domain - log.Printf("Waiting for server (%s.%s) to have to be provisioned", hostname, domain) - - stateConf := &resource.StateChangeConf{ - Pending: []string{"retry", "pending"}, - Target: []string{"provisioned"}, - Refresh: func() (interface{}, string, error) { - service := services.GetAccountService(meta.(ProviderConfig).SoftLayerSession()) - bms, err := service.Filter( - filter.Build( - filter.Path("hardware.hostname").Eq(hostname), - filter.Path("hardware.domain").Eq(domain), - ), - ).Mask("id,provisionDate").GetHardware() - if err != nil { - return false, "retry", nil - } - - if len(bms) == 0 || bms[0].ProvisionDate == nil { - return datatypes.Hardware{}, "pending", nil - } else { - return bms[0], "provisioned", nil - } - }, - Timeout: 4 * time.Hour, - Delay: 30 * time.Second, - MinTimeout: 2 * time.Minute, - } - - return stateConf.WaitForState() -} - -func waitForNoBareMetalQuoteActiveTransactions(id int, meta interface{}) (interface{}, error) { - log.Printf("Waiting for server (%d) to have zero active transactions", id) - service := services.GetHardwareServerService(meta.(ProviderConfig).SoftLayerSession()) - - stateConf := &resource.StateChangeConf{ - Pending: []string{"retry", "active"}, - Target: []string{"idle"}, - Refresh: func() (interface{}, string, error) { - bm, err := service.Id(id).Mask("id,activeTransactionCount").GetObject() - if err != nil { - return false, "retry", nil - } - - if bm.ActiveTransactionCount != nil && *bm.ActiveTransactionCount == 0 { - return bm, "idle", nil - } else { - return bm, "active", nil - } - }, - Timeout: 4 * time.Hour, - Delay: 5 * time.Second, - MinTimeout: 1 * time.Minute, - } - - return stateConf.WaitForState() -} - -// Depends on ressource_softlayer_bare_metal.go setHardwareTags - -// Depends on ressource_softlayer_bare_metal.go setHardwareNotes diff --git a/softlayer/resource_softlayer_bare_metal_quote_test.go b/softlayer/resource_softlayer_bare_metal_quote_test.go deleted file mode 100644 index 57ec459fa..000000000 --- a/softlayer/resource_softlayer_bare_metal_quote_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package softlayer - -import ( - "errors" - "fmt" - "strconv" - "testing" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" - "github.com/softlayer/softlayer-go/datatypes" - "github.com/softlayer/softlayer-go/services" - "github.com/softlayer/softlayer-go/sl" -) - -func TestAccSoftLayerBareMetalQuote_Basic(t *testing.T) { - var bareMetal datatypes.Hardware - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckSoftLayerBareMetalQuoteDestroy, - Steps: []resource.TestStep{ - { - Config: testAccCheckSoftLayerBareMetalQuoteConfig_basic, - Destroy: false, - Check: resource.ComposeTestCheckFunc( - testAccCheckSoftLayerBareMetalQuoteExists("softlayer_bare_metal_quote.terraform-acceptance-test-1", &bareMetal), - resource.TestCheckResourceAttr( - "softlayer_bare_metal_quote.terraform-acceptance-test-1", "hostname", "terraform-test"), - resource.TestCheckResourceAttr( - "softlayer_bare_metal_quote.terraform-acceptance-test-1", "domain", "bar.example.com"), - resource.TestCheckResourceAttr( - "softlayer_bare_metal_quote.terraform-acceptance-test-1", "user_metadata", "{\"value\":\"newvalue\"}"), - resource.TestCheckResourceAttr( - "softlayer_bare_metal_quote.terraform-acceptance-test-1", "quote_id", "2179879"), - CheckStringSet( - "softlayer_bare_metal_quote.terraform-acceptance-test-1", - "tags", []string{"collectd"}, - ), - ), - }, - - { - Config: testAccCheckSoftLayerBareMetalQuoteConfig_update, - Destroy: false, - Check: resource.ComposeTestCheckFunc( - testAccCheckSoftLayerBareMetalQuoteExists("softlayer_bare_metal_quote.terraform-acceptance-test-1", &bareMetal), - CheckStringSet( - "softlayer_bare_metal_quote.terraform-acceptance-test-1", - "tags", []string{"mesos-master"}, - ), - ), - }, - }, - }) -} - -func testAccCheckSoftLayerBareMetalQuoteDestroy(s *terraform.State) error { - service := services.GetHardwareService(testAccProvider.Meta().(ProviderConfig).SoftLayerSession()) - - for _, rs := range s.RootModule().Resources { - if rs.Type != "softlayer_bare_metal_quote" { - continue - } - - id, _ := strconv.Atoi(rs.Primary.ID) - - // Try to find the bare metal - _, err := service.Id(id).GetObject() - - // Wait - if err != nil { - if apiErr, ok := err.(sl.Error); !ok || apiErr.StatusCode != 404 { - return fmt.Errorf( - "Error waiting for bare metal (%d) to be destroyed: %s", - id, err) - } - } - } - - return nil -} - -func testAccCheckSoftLayerBareMetalQuoteExists(n string, bareMetal *datatypes.Hardware) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - if rs.Primary.ID == "" { - return errors.New("No bare metal ID is set") - } - - id, err := strconv.Atoi(rs.Primary.ID) - - if err != nil { - return err - } - - service := services.GetHardwareService(testAccProvider.Meta().(ProviderConfig).SoftLayerSession()) - bm, err := service.Id(id).GetObject() - if err != nil { - return err - } - - fmt.Printf("The ID is %d", *bm.Id) - - if *bm.Id != id { - return errors.New("Bare metal not found") - } - - *bareMetal = bm - - return nil - } -} - -const testAccCheckSoftLayerBareMetalQuoteConfig_basic = ` -resource "softlayer_bare_metal_quote" "terraform-acceptance-test-1" { - hostname = "terraform-test" - domain = "bar.example.com" - user_metadata = "{\"value\":\"newvalue\"}" - quote_id = 2179879 - tags = ["collectd"] -} -` - -const testAccCheckSoftLayerBareMetalQuoteConfig_update = ` -resource "softlayer_bare_metal_quote" "terraform-acceptance-test-1" { - hostname = "terraform-test" - domain = "bar.example.com" - user_metadata = "{\"value\":\"newvalue\"}" - quote_id = 2179879 - tags = ["mesos-master"] -} -` From 5a641c4285b6f85f3fdf2009a448782237baa7d9 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Sun, 9 Jul 2017 22:59:25 -0400 Subject: [PATCH 25/35] Updated doc for bare metal server. --- docs/resources/softlayer_bare_metal.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index 8f8635c2d..01dd4b0a1 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -128,8 +128,8 @@ resource "softlayer_bare_metal" "quote_test" { } ``` -Users can use additional options when they create a new bare metal server with `quote_id`. The folloing example defines target VLANs, subnets, - user meta data, and tags additionally. +Users can use additional options when they create a new bare metal server with `quote_id`. The following example defines target VLANs, subnets, + user metadata, and tags additionally. ### Example of a quote based ordering with additional options ```hcl @@ -225,7 +225,7 @@ The following arguments are supported: * It is a mandatory attribute for pre-set configuration bare metal server provisioning. * *Optional* -**Custom bare metal server / Quote based custom bare metal server provisionig attributes** +**Custom bare metal server / Quote based custom bare metal server provisioning attributes** * `public_vlan_id` | *int* * Public VLAN which is to be used for the public network interface of the instance. Accepted values can be found [here](https://control.softlayer.com/network/vlans). Click on the desired VLAN and note the id number in the URL. @@ -267,12 +267,12 @@ The following arguments are supported: * `public_bandwidth` can be greater than 0 when `private_network_only` is `false` and the server is a monthly based server. * *Optional* * `memory` | *int* - * Amount of memory(GB) for the server. + * An amount of memory(GB) for the server. * *Optional* * `storage_groups` | *array of storage group objects* * RAID and partition configuration. Refer to the [link](https://sldn.softlayer.com/blog/hansKristian/Ordering-RAID-through-API) to configure `storage_groups`. * *Optional* - * Each storage group object has the following sub attributes: + * Each storage group object has the following sub-attributes: * `array_type_id` | *int* * It provides RAID type. You can find `array_type_id` from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Configuration_Storage_Group_Array_Type/getAllObjects). * *Required* @@ -288,17 +288,17 @@ The following arguments are supported: * *Optional* * `redundant_power_supply` | *boolean* - * If `redundant_power_supply` is true, additional power supply will be provided. + * If `redundant_power_supply` is true, an additional power supply will be provided. * *Optional* * `tcp_monitoring` | *boolean* * If `tcp_monitoring` is `false`, ping monitoring service will be provided. If `tcp_monitoring` is `true`, ping and tcp monitoring service will be provided. * *Optional* -**Quote based probisioning only attributes** +**Quote based provisioning only attributes** * `quote_id` | *int* * Create a pre-set configured bare metal server or custom bare metal server using the quote. * If quote_id is defined, the terraform uses specifications in the quote to create a bare metal server. - * You can find the quote id by navigating on the portal to _Account > Sales > Quotes_ . + * You can find the quote id by navigating on the portal to _Account > Sales > Quotes_. * *Optional* ## Attributes Reference From e94cae468dbb6507c7ec5450ec0c808fd47a578b Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 10 Jul 2017 13:58:04 -0400 Subject: [PATCH 26/35] Added quote_bare_metal data source. --- .../data_source_softlayer_quote_bare_metal.go | 241 ++++++++++++++++++ softlayer/provider.go | 9 +- 2 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 softlayer/data_source_softlayer_quote_bare_metal.go diff --git a/softlayer/data_source_softlayer_quote_bare_metal.go b/softlayer/data_source_softlayer_quote_bare_metal.go new file mode 100644 index 000000000..700fe9ec6 --- /dev/null +++ b/softlayer/data_source_softlayer_quote_bare_metal.go @@ -0,0 +1,241 @@ +package softlayer + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/softlayer/softlayer-go/services" + "github.com/softlayer/softlayer-go/sl" + "strconv" + "strings" +) + +func dataSourceSoftLayerQuoteBareMetal() *schema.Resource { + return &schema.Resource{ + Read: dataSourceSoftLayerQuoteBareMetalRead, + + Schema: map[string]*schema.Schema{ + "id": { + Description: "The internal id of the quote for bare metal server", + Type: schema.TypeInt, + Computed: true, + }, + + "name": { + Description: "The name of this quote", + Type: schema.TypeString, + Required: true, + }, + + "os_reference_code": { + Type: schema.TypeString, + Computed: true, + }, + + "datacenter": { + Type: schema.TypeString, + Computed: true, + }, + + "network_speed": { + Type: schema.TypeInt, + Computed: true, + }, + + "private_network_only": { + Type: schema.TypeBool, + Computed: true, + }, + + "tcp_monitoring": { + Type: schema.TypeBool, + Computed: true, + }, + + "package_key_name": { + Type: schema.TypeString, + Computed: true, + }, + + "process_key_name": { + Type: schema.TypeString, + Computed: true, + }, + + "disk_key_names": { + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + + "redundant_network": { + Type: schema.TypeBool, + Computed: true, + }, + + "unbonded_network": { + Type: schema.TypeBool, + Computed: true, + }, + + "public_bandwidth": { + Type: schema.TypeInt, + Computed: true, + }, + + "memory": { + Type: schema.TypeInt, + Computed: true, + }, + + // Custom bare metal server only + "redundant_power_supply": { + Type: schema.TypeBool, + Computed: true, + }, + + // Custom bare metal server only - Order multiple RAID groups + "storage_groups": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "array_type_id": { + Type: schema.TypeInt, + Computed: true, + }, + "hard_drives": { + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeInt}, + Computed: true, + }, + "array_size": { + Type: schema.TypeInt, + Computed: true, + }, + "partition_template_id": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceSoftLayerQuoteBareMetalRead(d *schema.ResourceData, meta interface{}) error { + sess := meta.(ProviderConfig).SoftLayerSession() + service := services.GetAccountService(sess) + + name := d.Get("name").(string) + + quotes, err := service. + Mask("id,name,order[items[storageGroups,item],orderTopLevelItems]"). + GetActiveQuotes() + if err != nil { + return fmt.Errorf("Error looking up quote [%s]: %s", name, err) + } + + for _, quote := range quotes { + if quote.Name != nil && *quote.Name == name { + // Build a bare metal template from the quote. + order, err := services.GetBillingOrderQuoteService(sess). + Id(*quote.Id).GetRecalculatedOrderContainer(nil, sl.Bool(false)) + if err != nil { + return fmt.Errorf( + "Encountered problem trying to get the bare metal order template from quote: %s", err) + } + bmPackage, err := services.GetProductPackageService(sess). + Id(*order.PackageId).GetObject() + if err != nil { + return fmt.Errorf("Unable to find a package name from quote: %s", err) + } + if len(order.StorageGroups) > 0 { + storageGroups := make([]map[string]interface{}, 0, len(order.StorageGroups)) + for _, sg := range order.StorageGroups { + storageGroup := make(map[string]interface{}) + storageGroup["array_type_id"] = *sg.ArrayTypeId + if sg.ArraySize != nil { + storageGroup["array_size"] = *sg.ArraySize + } + storageGroup["partition_template_id"] = *sg.PartitionTemplateId + storageGroup["hard_drives"] = sg.HardDrives + storageGroups = append(storageGroups, storageGroup) + } + + d.Set("storage_groups", storageGroups) + } + locationId, err := strconv.Atoi(*order.Location) + if err != nil { + return fmt.Errorf("Location Id should be an integer: %s", *order.Location) + } + dc, err := services.GetLocationDatacenterService(sess).Id(locationId).GetObject() + if err != nil { + return fmt.Errorf("Unable to find a data center from quote: %s", err) + } + d.Set("datacenter", *dc.Name) + d.SetId(fmt.Sprintf("%d", *quote.Id)) + d.Set("package_key_name", *bmPackage.KeyName) + d.Set("redundant_power_supply", false) + diskMap := make(map[int]string) + + for _, item := range quote.Order.Items { + switch *item.CategoryCode { + case "server": + d.Set("process_key_name", *item.Item.KeyName) + case "os": + d.Set("os_reference_code", *item.Item.KeyName) + case "ram": + d.Set("memory", int(*item.Item.Capacity)) + case "bandwidth": + d.Set("public_bandwidth", int(*item.Item.Capacity)) + case "port_speed": + d.Set("network_speed", int(*item.Item.Capacity)) + d.Set("unbonded_network", false) + d.Set("redundant_network", false) + d.Set("private_network_only", false) + if strings.Contains(*item.Item.KeyName, "UNBONDED") { + d.Set("unbonded_network", true) + } + if strings.Contains(*item.Item.KeyName, "REDUNDANT") { + d.Set("redundant_network", true) + } + if !strings.Contains(*item.Item.KeyName, "PUBLIC") { + d.Set("private_network_only", true) + } + case "power_supply": + d.Set("redundant_power_supply", true) + case "monitoring": + d.Set("tcp_monitoring", false) + if strings.Contains(*item.Item.KeyName, "TCP") { + d.Set("tcp_monitoring", true) + } + } + + if strings.HasPrefix(*item.CategoryCode, "disk") { + diskIndex, err := strconv.Atoi(strings.Split(*item.CategoryCode, "disk")[1]) + if err == nil { + diskMap[diskIndex] = *item.Item.KeyName + } + } + + } + numberOfDisk := len(diskMap) + if numberOfDisk > 0 { + disks := make([]string, numberOfDisk, numberOfDisk) + for i := 0; i < numberOfDisk; i++ { + if len(diskMap[i]) > 0 { + disks[i] = diskMap[i] + } else { + return fmt.Errorf("Unable to retrieve disk information.") + } + } + d.Set("disk_key_names", disks) + } + return nil + } + } + + return fmt.Errorf("Could not find quote with name [%s]", name) +} diff --git a/softlayer/provider.go b/softlayer/provider.go index c331e467a..b8a5d43b0 100644 --- a/softlayer/provider.go +++ b/softlayer/provider.go @@ -46,10 +46,11 @@ func Provider() terraform.ResourceProvider { }, DataSourcesMap: map[string]*schema.Resource{ - "softlayer_ssh_key": dataSourceSoftLayerSSHKey(), - "softlayer_image_template": dataSourceSoftLayerImageTemplate(), - "softlayer_vlan": dataSourceSoftLayerVlan(), - "softlayer_dns_domain": dataSourceSoftLayerDnsDomain(), + "softlayer_ssh_key": dataSourceSoftLayerSSHKey(), + "softlayer_image_template": dataSourceSoftLayerImageTemplate(), + "softlayer_vlan": dataSourceSoftLayerVlan(), + "softlayer_dns_domain": dataSourceSoftLayerDnsDomain(), + "softlayer_quote_bare_metal": dataSourceSoftLayerQuoteBareMetal(), }, ResourcesMap: map[string]*schema.Resource{ From 68b123cb7c20ce689abf7a936054e5c4097ebac8 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 10 Jul 2017 14:44:52 -0400 Subject: [PATCH 27/35] Added test code for quote_bare_metal. --- .../data_source_softlayer_quote_bare_metal.go | 8 ++-- ..._source_softlayer_quote_bare_metal_test.go | 43 +++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 softlayer/data_source_softlayer_quote_bare_metal_test.go diff --git a/softlayer/data_source_softlayer_quote_bare_metal.go b/softlayer/data_source_softlayer_quote_bare_metal.go index 700fe9ec6..5d0f3b435 100644 --- a/softlayer/data_source_softlayer_quote_bare_metal.go +++ b/softlayer/data_source_softlayer_quote_bare_metal.go @@ -111,10 +111,12 @@ func dataSourceSoftLayerQuoteBareMetal() *schema.Resource { }, "array_size": { Type: schema.TypeInt, + Optional: true, Computed: true, }, "partition_template_id": { Type: schema.TypeInt, + Optional: true, Computed: true, }, }, @@ -156,10 +158,8 @@ func dataSourceSoftLayerQuoteBareMetalRead(d *schema.ResourceData, meta interfac for _, sg := range order.StorageGroups { storageGroup := make(map[string]interface{}) storageGroup["array_type_id"] = *sg.ArrayTypeId - if sg.ArraySize != nil { - storageGroup["array_size"] = *sg.ArraySize - } - storageGroup["partition_template_id"] = *sg.PartitionTemplateId + storageGroup["array_size"] = sl.Get(sg.ArraySize, 0) + storageGroup["partition_template_id"] = sl.Get(sg.PartitionTemplateId, 0) storageGroup["hard_drives"] = sg.HardDrives storageGroups = append(storageGroups, storageGroup) } diff --git a/softlayer/data_source_softlayer_quote_bare_metal_test.go b/softlayer/data_source_softlayer_quote_bare_metal_test.go new file mode 100644 index 000000000..7a7b2c943 --- /dev/null +++ b/softlayer/data_source_softlayer_quote_bare_metal_test.go @@ -0,0 +1,43 @@ +package softlayer + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccSoftLayerQuoteBareMetalDataSource_Basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckSoftLayerQuoteBareMetalDataSourceConfig_basic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "data.softlayer_quote_bare_metal.test_quote_bm", + "package_key_name", + "4U_DUAL_E52600_36_DRIVES", + ), + resource.TestCheckResourceAttr( + "data.softlayer_quote_bare_metal.test_quote_bm", + "process_key_name", + "INTEL_XEON_2650_2_00", + ), + resource.TestCheckResourceAttr( + "data.softlayer_quote_bare_metal.test_quote_bm", + "datacenter", + "dal06", + ), + ), + }, + }, + }) +} + +// The datasource to apply +const testAccCheckSoftLayerQuoteBareMetalDataSourceConfig_basic = ` +data "softlayer_quote_bare_metal" "test_quote_bm" { + name = "test" +} +` From c45aa1d55996b692f7a09c873e70f63144a3b0b8 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 10 Jul 2017 14:59:05 -0400 Subject: [PATCH 28/35] Added doc for quote_bare_metal data source. --- .../datasources/softlayer_quote_bare_metal.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docs/datasources/softlayer_quote_bare_metal.md diff --git a/docs/datasources/softlayer_quote_bare_metal.md b/docs/datasources/softlayer_quote_bare_metal.md new file mode 100644 index 000000000..010dd5102 --- /dev/null +++ b/docs/datasources/softlayer_quote_bare_metal.md @@ -0,0 +1,36 @@ +# `softlayer_quote_bare_metal` + +Use this data source to import the name of an *existing* custom bare metal quote as a read-only data source. + +## Example Usage + +```hcl +data softlayer_quote_bare_metal quote_test{ + name = "quote_test" +} +``` + +It imports the quote of the custom bare metal server and shows detailed attributes. + + +## Argument Reference + +`name` - (Required) The name of the quote, as it was defined in SoftLayer + +## Attributes Reference + +`id` - Set to the ID of the quote. +`datacenter` - It specifies which datacenter the instance is to be provisioned in. +`os_reference_code` - Target OS key name. +`network_speed` - Specifies the connection speed (in Mbps) for the instance's network components. +`private_network_only` - Specifies whether or not the instance only has access to the private network. +`package_key_name` - Custom bare metal server's package key name. +`process_key_name` - Custom bare metal server's process key name. +`disk_key_names` - Array of internal disk key names. +`redundant_network` - If `redundant_network` is `true`, two physical network interfaces will be provided with a bonding configuration. +`unbonded_network` - If `unbonded_network` is `true`, two physical network interfaces will be provided. +`public_bandwidth` - Allowed public network traffic(GB) per month. +`memory` - An amount of memory(GB) for the server. +`storage_groups` - RAID and partition configuration. +`redundant_power_supply`- If `redundant_power_supply` is true, an additional power supply will be provided. +`tcp_monitoring` - If `tcp_monitoring` is `true`, ping and tcp monitoring service will be provided. \ No newline at end of file From 7dde27cb8b31994a2621dd45569bcaad1c295c29 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 10 Jul 2017 18:35:25 -0400 Subject: [PATCH 29/35] Added test for bare metal. --- .../data_source_softlayer_quote_bare_metal.go | 2 +- softlayer/resource_softlayer_bare_metal.go | 4 +- .../resource_softlayer_bare_metal_test.go | 82 +++++++++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/softlayer/data_source_softlayer_quote_bare_metal.go b/softlayer/data_source_softlayer_quote_bare_metal.go index 5d0f3b435..4987142cd 100644 --- a/softlayer/data_source_softlayer_quote_bare_metal.go +++ b/softlayer/data_source_softlayer_quote_bare_metal.go @@ -96,7 +96,7 @@ func dataSourceSoftLayerQuoteBareMetal() *schema.Resource { // Custom bare metal server only - Order multiple RAID groups "storage_groups": { - Type: schema.TypeSet, + Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 6b408010b..aff63a1b5 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -219,7 +219,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { // Custom bare metal server only - Order multiple RAID groups "storage_groups": { - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, ForceNew: true, Elem: &schema.Resource{ @@ -609,7 +609,7 @@ func resourceSoftLayerBareMetalDelete(d *schema.ResourceData, meta interface{}) billingItemService := services.GetBillingItemService(sess) _, err = billingItemService.Id(*billingItem.Id).CancelItem( - sl.Bool(true), sl.Bool(true), sl.String("No longer required"), sl.String("Please cancel this server"), + sl.Bool(d.Get("hourly_billing").(bool)), sl.Bool(true), sl.String("No longer required"), sl.String("Please cancel this server"), ) if err != nil { return fmt.Errorf("Error canceling the bare metal server (%d): %s", id, err) diff --git a/softlayer/resource_softlayer_bare_metal_test.go b/softlayer/resource_softlayer_bare_metal_test.go index 46a01fbba..26f88541e 100644 --- a/softlayer/resource_softlayer_bare_metal_test.go +++ b/softlayer/resource_softlayer_bare_metal_test.go @@ -66,6 +66,62 @@ func TestAccSoftLayerBareMetal_Basic(t *testing.T) { }) } +func TestAccSoftLayerBareMetalQuote_Basic(t *testing.T) { + var bareMetal datatypes.Hardware + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSoftLayerBareMetalDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckSoftLayerBareMetalQuoteConfig_basic, + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testAccCheckSoftLayerBareMetalExists("softlayer_bare_metal.terraform-acceptance-test-2", &bareMetal), + resource.TestCheckResourceAttr( + "softlayer_bare_metal.terraform-acceptance-test-2", "hostname", "terraform-test2"), + resource.TestCheckResourceAttr( + "softlayer_bare_metal.terraform-acceptance-test-2", "domain", "bar.example.com"), + resource.TestCheckResourceAttr( + "softlayer_bare_metal.terraform-acceptance-test-2", "user_metadata", "{\"value\":\"newvalue\"}"), + resource.TestCheckResourceAttr( + "softlayer_bare_metal.terraform-acceptance-test-2", "quote_id", "2179879"), + CheckStringSet( + "softlayer_bare_metal.terraform-acceptance-test-2", + "tags", []string{"collectd"}, + ), + ), + }, + }, + }) +} + +func TestAccSoftLayerBareMetalCustom_Quote(t *testing.T) { + var bareMetal datatypes.Hardware + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSoftLayerBareMetalDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckSoftLayerBareMetalCustom_basic, + Destroy: false, + Check: resource.ComposeTestCheckFunc( + testAccCheckSoftLayerBareMetalExists("softlayer_bare_metal.terraform-acceptance-test-3", &bareMetal), + resource.TestCheckResourceAttr( + "softlayer_bare_metal.terraform-acceptance-test-3", "memory", "32"), + resource.TestCheckResourceAttr( + "softlayer_bare_metal.terraform-acceptance-test-3", "network_speed", "1000"), + resource.TestCheckResourceAttr( + "softlayer_bare_metal.terraform-acceptance-test-3", "public_bandwidth", "500"), + ), + }, + }, + }) +} + func testAccCheckSoftLayerBareMetalDestroy(s *terraform.State) error { service := services.GetHardwareService(testAccProvider.Meta().(ProviderConfig).SoftLayerSession()) @@ -156,3 +212,29 @@ resource "softlayer_bare_metal" "terraform-acceptance-test-1" { tags = ["mesos-master"] } ` + +const testAccCheckSoftLayerBareMetalQuoteConfig_basic = ` +resource "softlayer_bare_metal" "terraform-acceptance-test-2" { + hostname = "terraform-test2" + domain = "bar.example.com" + user_metadata = "{\"value\":\"newvalue\"}" + quote_id = 2179879 + tags = ["collectd"] +} +` + +const testAccCheckSoftLayerBareMetalCustom_basic = ` +resource "softlayer_bare_metal" "terraform-acceptance-test-3" { + package_key_name = "2U_DUAL_E52600_12_DRIVES" + process_key_name = "INTEL_DUAL_INTEL_XEON_E52620_2_00" + memory = 32 + os_reference_code = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" + hostname = "cust-bm" + domain = "ms.com" + datacenter = "dal05" + network_speed = 1000 + public_bandwidth = 500 + disk_key_names = [ "HARD_DRIVE_1_00_TB_SATA_2", "HARD_DRIVE_1_00_TB_SATA_2" ] + hourly_billing = false +} +` From 1c33a9f20d2dcc5bba32a14ef02e61296d6f9936 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Tue, 11 Jul 2017 13:10:29 -0400 Subject: [PATCH 30/35] Fixed bugs for bare metal server. --- softlayer/resource_softlayer_bare_metal.go | 18 +++++++++--------- .../resource_softlayer_bare_metal_test.go | 3 ++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index aff63a1b5..0e4732002 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -521,11 +521,10 @@ func resourceSoftLayerBareMetalRead(d *schema.ResourceData, meta interface{}) er d.Set("notes", sl.Get(result.Notes, nil)) d.Set("memory", *result.MemoryCapacity) + d.Set("redundant_power_supply", false) if *result.PowerSupplyCount == 2 { d.Set("redundant_power_supply", true) - } else { - d.Set("redundant_power_supply", false) } d.Set("public_bandwidth", int(*result.BandwidthAllocation)) @@ -607,6 +606,7 @@ func resourceSoftLayerBareMetalDelete(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error getting billing item for bare metal server: %s", err) } + // Monthly bare metal servers only support an anniversary date cancellation option. billingItemService := services.GetBillingItemService(sess) _, err = billingItemService.Id(*billingItem.Id).CancelItem( sl.Bool(d.Get("hourly_billing").(bool)), sl.Bool(true), sl.String("No longer required"), sl.String("Please cancel this server"), @@ -915,12 +915,12 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype } // Add storage_groups for RAID configuration + diskController, err := getItemPriceId(items, "disk_controller", "DISK_CONTROLLER_RAID") + if err != nil { + return datatypes.Container_Product_Order{}, err + } + order.Prices = append(order.Prices, diskController) if _, ok := d.GetOk("storage_groups"); ok { - diskController, err := getItemPriceId(items, "disk_controller", "DISK_CONTROLLER_RAID") - if err != nil { - return datatypes.Container_Product_Order{}, err - } - order.Prices = append(order.Prices, diskController) order.StorageGroups = getStorageGroupsFromResourceData(d) } @@ -1097,7 +1097,7 @@ func getPackageByModel(sess *session.Session, model string) (datatypes.Product_P } func getStorageGroupsFromResourceData(d *schema.ResourceData) []datatypes.Container_Product_Order_Storage_Group { - storageGroupLists := d.Get("storage_groups").(*schema.Set).List() + storageGroupLists := d.Get("storage_groups").([]interface{}) storageGroups := make([]datatypes.Container_Product_Order_Storage_Group, 0) for _, storageGroupList := range storageGroupLists { @@ -1105,7 +1105,7 @@ func getStorageGroupsFromResourceData(d *schema.ResourceData) []datatypes.Contai var storageGroupObj datatypes.Container_Product_Order_Storage_Group storageGroupObj.ArrayTypeId = sl.Int(storageGroup["array_type_id"].(int)) hardDrives := storageGroup["hard_drives"].([]interface{}) - storageGroupObj.HardDrives = make([]int, 0) + storageGroupObj.HardDrives = make([]int, 0, len(hardDrives)) for _, hardDrive := range hardDrives { storageGroupObj.HardDrives = append(storageGroupObj.HardDrives, hardDrive.(int)) } diff --git a/softlayer/resource_softlayer_bare_metal_test.go b/softlayer/resource_softlayer_bare_metal_test.go index 26f88541e..f0b90f833 100644 --- a/softlayer/resource_softlayer_bare_metal_test.go +++ b/softlayer/resource_softlayer_bare_metal_test.go @@ -97,7 +97,7 @@ func TestAccSoftLayerBareMetalQuote_Basic(t *testing.T) { }) } -func TestAccSoftLayerBareMetalCustom_Quote(t *testing.T) { +func TestAccSoftLayerBareMetalCustom_Basic(t *testing.T) { var bareMetal datatypes.Hardware resource.Test(t, resource.TestCase{ @@ -236,5 +236,6 @@ resource "softlayer_bare_metal" "terraform-acceptance-test-3" { public_bandwidth = 500 disk_key_names = [ "HARD_DRIVE_1_00_TB_SATA_2", "HARD_DRIVE_1_00_TB_SATA_2" ] hourly_billing = false + redundant_power_supply = true } ` From 72c5354ff50bdf21c617d169226024b059f9bd49 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Wed, 12 Jul 2017 19:14:03 -0400 Subject: [PATCH 31/35] Fixed bugs in the bare metal server. --- Makefile | 2 +- docs/resources/softlayer_bare_metal.md | 2 + .../data_source_softlayer_quote_bare_metal.go | 12 ++-- softlayer/resource_softlayer_bare_metal.go | 71 ++++++++++++------- .../resource_softlayer_bare_metal_test.go | 2 +- 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index 733f567d3..9dd9528d0 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ test: fmtcheck vet # testacc runs acceptance tests # e.g make testacc TESTARGS="-run TestAccSoftLayerScaleGroup_Basic" testacc: fmtcheck vet - TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m + TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 1440m # testrace runs the race checker testrace: fmtcheck vet diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index 01dd4b0a1..74a5898e3 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -114,6 +114,8 @@ resource "softlayer_bare_metal" "custom_bm1" { } ``` +_Please Note_: Custom bare metal servers does not support `immediate cancellation`. If the custom bare metal server is deleted by terraform, `anniversary date cancellation` option will be used. + ## Create a bare metal server using quote ID If users already have a quote id for the bare metal server, they can create a new bare metal server with the quote id. The following example describes a basic configuration for a bare metal server with quote_id. diff --git a/softlayer/data_source_softlayer_quote_bare_metal.go b/softlayer/data_source_softlayer_quote_bare_metal.go index 4987142cd..ea9e8f17f 100644 --- a/softlayer/data_source_softlayer_quote_bare_metal.go +++ b/softlayer/data_source_softlayer_quote_bare_metal.go @@ -27,11 +27,6 @@ func dataSourceSoftLayerQuoteBareMetal() *schema.Resource { Required: true, }, - "os_reference_code": { - Type: schema.TypeString, - Computed: true, - }, - "datacenter": { Type: schema.TypeString, Computed: true, @@ -62,6 +57,11 @@ func dataSourceSoftLayerQuoteBareMetal() *schema.Resource { Computed: true, }, + "os_key_name": { + Type: schema.TypeString, + Computed: true, + }, + "disk_key_names": { Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, @@ -185,7 +185,7 @@ func dataSourceSoftLayerQuoteBareMetalRead(d *schema.ResourceData, meta interfac case "server": d.Set("process_key_name", *item.Item.KeyName) case "os": - d.Set("os_reference_code", *item.Item.KeyName) + d.Set("os_key_name", *item.Item.KeyName) case "ram": d.Set("memory", int(*item.Item.Capacity)) case "bandwidth": diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 0e4732002..ef929ec9d 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -101,11 +101,12 @@ func resourceSoftLayerBareMetal() *schema.Resource { // Pe-set configured / custom bare metal server - Mandatory attribute "os_reference_code": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"image_template_id"}, + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"image_template_id"}, + DiffSuppressFunc: applyOnce, }, "image_template_id": { @@ -149,9 +150,11 @@ func resourceSoftLayerBareMetal() *schema.Resource { // Pre-set configured / custom bare metal server "tcp_monitoring": { - Type: schema.TypeBool, - Optional: true, - Default: false, + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + DiffSuppressFunc: applyOnce, }, // Custom bare metal server - Mandatory attribute @@ -170,6 +173,14 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, + // Custom bare metal server - Mandatory attribute + "os_key_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, + }, + // Custom bare metal server - Mandatory attribute "disk_key_names": { Type: schema.TypeList, @@ -197,9 +208,11 @@ func resourceSoftLayerBareMetal() *schema.Resource { // Custom bare metal server only "public_bandwidth": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, + Type: schema.TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + DiffSuppressFunc: applyOnce, }, // Custom bare metal server only @@ -431,7 +444,7 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) } log.Println("[INFO] Ordering bare metal server") - _, err = services.GetProductOrderService(sess).PlaceOrder(&order, sl.Bool(true)) + _, err = services.GetProductOrderService(sess).PlaceOrder(&order, sl.Bool(false)) if err != nil { return fmt.Errorf("Error ordering bare metal server: %s\n%+v\n", err, order) } @@ -480,10 +493,9 @@ func resourceSoftLayerBareMetalRead(d *schema.ResourceData, meta interface{}) er "hourlyBillingFlag," + "datacenter[id,name,longName]," + "primaryNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed]," + - "primaryBackendNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed]," + - "memoryCapacity,powerSupplyCount,bandwidthAllocation," + - "operatingSystem[softwareLicense[softwareDescription[referenceCode]]]," + - "backendNetworkComponentCount,primaryBackendNetworkComponent[networkVlanTrunkCount]", + "primaryBackendNetworkComponent[networkVlan[id,primaryRouter,vlanNumber],maxSpeed,redundancyEnabledFlag]," + + "memoryCapacity,powerSupplyCount," + + "operatingSystem[softwareLicense[softwareDescription[referenceCode]]]", ).GetObject() if err != nil { @@ -527,12 +539,21 @@ func resourceSoftLayerBareMetalRead(d *schema.ResourceData, meta interface{}) er d.Set("redundant_power_supply", true) } - d.Set("public_bandwidth", int(*result.BandwidthAllocation)) - d.Set("redundant_network", false) d.Set("unbonded_network", false) - if *result.BackendNetworkComponentCount > 2 && result.PrimaryBackendNetworkComponent != nil { - if *result.PrimaryBackendNetworkComponent.NetworkVlanTrunkCount > 0 { + + backendNetworkComponent, err := service.Filter( + filter.Build( + filter.Path("backendNetworkComponents.status").Eq("ACTIVE"), + ), + ).Id(id).GetBackendNetworkComponents() + + if err != nil { + return fmt.Errorf("Error retrieving bare metal server network: %s", err) + } + + if len(backendNetworkComponent) > 2 && result.PrimaryBackendNetworkComponent != nil { + if *result.PrimaryBackendNetworkComponent.RedundancyEnabledFlag { d.Set("redundant_network", true) } else { d.Set("unbonded_network", true) @@ -668,7 +689,7 @@ func waitForBareMetalProvision(d *datatypes.Hardware, meta interface{}) (interfa }, Timeout: 24 * time.Hour, Delay: 60 * time.Second, - MinTimeout: 2 * time.Minute, + MinTimeout: 1 * time.Minute, NotFoundChecks: 24 * 60, } @@ -696,7 +717,7 @@ func waitForNoBareMetalActiveTransactions(id int, meta interface{}) (interface{} }, Timeout: 24 * time.Hour, Delay: 60 * time.Second, - MinTimeout: 2 * time.Minute, + MinTimeout: 1 * time.Minute, NotFoundChecks: 24 * 60, } @@ -772,9 +793,9 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'datacenter' is not defined.") } - osReferenceCode, ok := d.GetOk("os_reference_code") + osKeyName, ok := d.GetOk("os_key_name") if !ok { - return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'os_reference_code' is not defined.") + return datatypes.Container_Product_Order{}, fmt.Errorf("The attribute 'os_key_name' is not defined.") } dc, err := location.GetDatacenterByName(sess, datacenter.(string), "id") @@ -803,7 +824,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype if err != nil { return datatypes.Container_Product_Order{}, err } - os, err := getItemPriceId(items, "os", osReferenceCode.(string)) + os, err := getItemPriceId(items, "os", osKeyName.(string)) if err != nil { return datatypes.Container_Product_Order{}, err } diff --git a/softlayer/resource_softlayer_bare_metal_test.go b/softlayer/resource_softlayer_bare_metal_test.go index f0b90f833..1310c66d8 100644 --- a/softlayer/resource_softlayer_bare_metal_test.go +++ b/softlayer/resource_softlayer_bare_metal_test.go @@ -230,7 +230,7 @@ resource "softlayer_bare_metal" "terraform-acceptance-test-3" { memory = 32 os_reference_code = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" hostname = "cust-bm" - domain = "ms.com" + domain = "example.com" datacenter = "dal05" network_speed = 1000 public_bandwidth = 500 From 3159ebcfd7e6c2bd383fe5d14b5f301917225a8c Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Wed, 12 Jul 2017 23:24:28 -0400 Subject: [PATCH 32/35] Simplified bare metal server types. --- docs/resources/softlayer_bare_metal.md | 87 +++++++++---------- .../data_source_softlayer_quote_bare_metal.go | 2 - softlayer/resource_softlayer_bare_metal.go | 53 +++++------ 3 files changed, 66 insertions(+), 76 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index 74a5898e3..f0457493b 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -1,18 +1,17 @@ # `softlayer_bare_metal` -Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. `softlayer_bare_metal` resource supports both pre-set configured bare metal servers and custom bare metal servers. +Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. `softlayer_bare_metal` resource supports both monthly bare metal servers and hourly bare metal servers. For more detail on bare metal types, refer to the [link](https://www.ibm.com/cloud-computing/bluemix/bare-metal-servers) -## Pre-set configured bare metal server -If the `softlayer_bare_metal` resource definition has a `fixed_config_preset` attribute, terraform will create a pre-set configured -bare metal server. The following example creates a new pre-set configured bare metal server with an hourly option. Hardware specifications +## Hourly bare metal server +If the `softlayer_bare_metal` resource definition has a `fixed_config_preset` attribute, terraform will create an hourly +bare metal server. The following example creates a new hourly bare metal server. Hardware specifications are already defined in the `fixed_config_preset` attribute and cannot be modified. -### Example of a pre-set configured bare metal server +### Example of an hourly bare metal server ```hcl -# Create a new bare metal -resource "softlayer_bare_metal" "pre-configured-bm1" { - hostname = "pre-configured-bm1" +resource "softlayer_bare_metal" "hourly-bm1" { + hostname = "hourly-bm1" domain = "example.com" os_reference_code = "UBUNTU_16_64" datacenter = "dal01" @@ -25,11 +24,10 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { In addition, users can use configure optional attributes such as `user_metadata`, `tags`, and `notes` attributes as follows: -### Example of additional attributes for the pre-set configured bare metal server +### Example of additional attributes for the hourly bare metal server ```hcl -# Create a new bare metal -resource "softlayer_bare_metal" "pre-configured-bm1" { - hostname = "pre-configured-bm1" +resource "softlayer_bare_metal" "hourly-bm1" { + hostname = "hourly-bm1" domain = "example.com" os_reference_code = "UBUNTU_16_64" datacenter = "dal01" @@ -47,19 +45,19 @@ resource "softlayer_bare_metal" "pre-configured-bm1" { } ``` -## Custom bare metal server -If the `fixed_config_preset` attribute is not configured, terraform will consider it as a monthly custom bare metal server resource. It provides -options to configure process, memory, network, disk, and RAID. Users also can assign VLANs and subnets for the target custom bare metal server. To configure the custom bare -metal server, you need to configure `package_key_name`, `proecss_key_name`, and `disk_key_names`. The following example describes a basic configuration - of the custom bare metal server. +## Monthly bare metal server +If the `fixed_config_preset` attribute is not configured, terraform will consider it as a monthly bare metal server resource. It provides +options to configure process, memory, network, disk, and RAID. Users also can assign VLANs and subnets for the target monthly bare metal server. To configure the monthly bare +metal server, you need to configure `package_key_name`, `proecss_key_name`, `disk_key_names`, and `os_key_name`. The following example describes a basic configuration + of the monthly bare metal server. -### Example of a custom bare metal server +### Example of a monthly bare metal server ```hcl -resource "softlayer_bare_metal" "custom_bm1" { +resource "softlayer_bare_metal" "monthly_bm1" { package_key_name = "DUAL_E52600_V4_12_DRIVES" process_key_name = "INTEL_INTEL_XEON_E52620_V4_2_10" memory = 64 - os_reference_code = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" + os_key_name = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" hostname = "cust-bm" domain = "ms.com" datacenter = "wdc04" @@ -72,15 +70,15 @@ resource "softlayer_bare_metal" "custom_bm1" { Users can configure additional options. The following example configures target VLANs, subnets, and a RAID controller. `storage_groups` configures RAIDs and disk partitioning. -### Example of a custom bare metal server with additional options +### Example of a monthly bare metal server with additional options ```hcl -resource "softlayer_bare_metal" "custom_bm1" { +resource "softlayer_bare_metal" "monthly_bm1" { # Mandatory attributes package_key_name = "DUAL_E52600_V4_12_DRIVES" process_key_name = "INTEL_INTEL_XEON_E52620_V4_2_10" memory = 64 - os_reference_code = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" + os_key_name = "OS_WINDOWS_2012_R2_FULL_DC_64_BIT_2" hostname = "cust-bm" domain = "ms.com" datacenter = "wdc04" @@ -114,7 +112,7 @@ resource "softlayer_bare_metal" "custom_bm1" { } ``` -_Please Note_: Custom bare metal servers does not support `immediate cancellation`. If the custom bare metal server is deleted by terraform, `anniversary date cancellation` option will be used. +_Please Note_: Monthly bare metal servers does not support `immediate cancellation`. If the monthly bare metal server is deleted by terraform, `anniversary date cancellation` option will be used. ## Create a bare metal server using quote ID If users already have a quote id for the bare metal server, they can create a new bare metal server with the quote id. The following example describes a basic configuration for a bare metal server with @@ -160,8 +158,6 @@ resource "softlayer_bare_metal" "quote_test" { The following arguments are supported: -**Common attributes** - * `hostname` | *string* * Hostname for the computing instance. * **Optional** @@ -187,23 +183,16 @@ The following arguments are supported: * Set tags on this bare metal server. The characters permitted are A-Z, 0-9, whitespace, _ (underscore), - (hyphen), . (period), and : (colon). All other characters will be stripped away. * *Optional* -**Pre-set configured bare metal server / custom bare metal server attributes** +**Monthly/Hourly bare metal server attributes** * `datacenter` | *string* * Specifies which datacenter the instance is to be provisioned in. - * It is a mandatory attribute for pre-set configured and custom bare metal servers. + * It is a mandatory attribute for monthly and hourly bare metal servers. * *Optional* * `hourly_billing` | *boolean* * Specifies the billing type for the instance. When true the computing instance will be billed on hourly usage, otherwise it will be billed on a monthly basis. - * Only pre-set configuration bare metal servers support hourly billing. * *Default*: true * *Optional* -* `os_reference_code` | *string* - * An operating system reference code that will be used to provision the computing instance. - * Pre-set configured bare metal server : [Get a complete list of the os reference codes available for pre-set configuration bare metal servers](https://api.softlayer.com/rest/v3/SoftLayer_Virtual_Guest_Block_Device_Template_Group/getVhdImportSoftwareDescriptions.json?objectMask=referenceCode) (use your api key as the password). - * Custom bare metal server : Note the package key ID from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}) and replace **PACKAGE_ID** in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/PACKAGE_ID/getItems?objectMask=mask[prices[id,categories[id,name,categoryCode],capacityRestrictionType,capacityRestrictionMinimum,capacityRestrictionMaximum,locationGroupId]]) to your package key ID. Select a OS key name from available OS key names. - * *Optional* - * **Conflicts with** `image_template_id`. * `image_template_id` | *int* * The image template id to be used to provision the computing instance. Note this is not the global identifier (uuid), but the image template group id that should point to a valid global identifier. You can get the image template id by navigating on the portal to _Devices > Manage > Images_, clicking on the desired image, and taking note of the id number in the browser URL location. * *Optional* @@ -220,14 +209,18 @@ The following arguments are supported: * *Default*: False * *Optional* -**Pre-set configured bare metal server only attributes** +**Hourly bare metal server only attributes** * `fixed_config_preset` | *string* - * The configuration preset that the pre-set configuration bare metal server will be provisioned with. This governs the type of cpu, number of cores, amount of ram, and hard drives which the bare metal server will have. [Take a look at the available presets](https://api.softlayer.com/rest/v3/SoftLayer_Hardware/getCreateObjectOptions.json) (use your api key as the password), and find the key called _fixedConfigurationPresets_. Under that, the presets will be identified by the *keyName*s. - * It is a mandatory attribute for pre-set configuration bare metal server provisioning. + * The configuration preset that the hourly bare metal server will be provisioned with. This governs the type of cpu, number of cores, amount of ram, and hard drives which the bare metal server will have. [Take a look at the available presets](https://api.softlayer.com/rest/v3/SoftLayer_Hardware/getCreateObjectOptions.json) (use your api key as the password), and find the key called _fixedConfigurationPresets_. Under that, the presets will be identified by the *keyName*s. + * It is a mandatory attribute for hourly bare metal server provisioning. * *Optional* - -**Custom bare metal server / Quote based custom bare metal server provisioning attributes** +* `os_reference_code` | *string* + * An operating system reference code that will be used to provision the computing instance. + * Hourly bare metal server : [Get a complete list of the os reference codes available for hourly bare metal servers](https://api.softlayer.com/rest/v3/SoftLayer_Virtual_Guest_Block_Device_Template_Group/getVhdImportSoftwareDescriptions.json?objectMask=referenceCode) (use your api key as the password). + * *Optional* + * **Conflicts with** `image_template_id`. +**Monthly / Quote based bare metal server provisioning attributes** * `public_vlan_id` | *int* * Public VLAN which is to be used for the public network interface of the instance. Accepted values can be found [here](https://control.softlayer.com/network/vlans). Click on the desired VLAN and note the id number in the URL. @@ -242,20 +235,24 @@ The following arguments are supported: * Private subnet which is to be used for the private network interface of the instance. Accepted values are primary private networks and can be found [here](https://control.softlayer.com/network/subnets). * *Optional* -**Custom bare metal server only attributes** +**Monthly bare metal server only attributes** * `package_key_name` | *string* - * Custom bare metal server's package key name. This attribute is only used when a new custom bare metal server is created. + * Monthly bare metal server's package key name. This attribute is only used when a new monthly bare metal server is created. * You can find available key names in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}). You need your username and api_key to access to the page. * *Optional* * `process_key_name` | *string* - * Custom bare metal server's process key name. This attribute is only used when a new custom bare metal server is created. + * Monthly bare metal server's process key name. This attribute is only used when a new monthly bare metal server is created. * Note the package key ID from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}) and replace **PACKAGE_ID** in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/PACKAGE_ID/getItems?objectMask=mask[prices[id,categories[id,name,categoryCode],capacityRestrictionType,capacityRestrictionMinimum,capacityRestrictionMaximum,locationGroupId]]) to your package key ID. Select a process key name from available process key names. * *Optional* * `disk_key_names` | *list* - * Array of internal disk key names. This attribute is only used when a new custom bare metal server is created. + * Array of internal disk key names. This attribute is only used when a new monthly bare metal server is created. * Note the package key ID from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}) and replace **PACKAGE_ID** in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/PACKAGE_ID/getItems?objectMask=mask[prices[id,categories[id,name,categoryCode],capacityRestrictionType,capacityRestrictionMinimum,capacityRestrictionMaximum,locationGroupId]]) to your package key ID. Select a disk key name from available disk key names. * *Optional* +* `os_key_name` | *string* + * An operating system key name that will be used to provision the computing instance. + * Note the package key ID from the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/getAllObjects?objectFilter={"type":{"keyName":{"operation":"BARE_METAL_CPU"}}}) and replace **PACKAGE_ID** in the [link](https://api.softlayer.com/rest/v3/SoftLayer_Product_Package/PACKAGE_ID/getItems?objectMask=mask[prices[id,categories[id,name,categoryCode],capacityRestrictionType,capacityRestrictionMinimum,capacityRestrictionMaximum,locationGroupId]]) to your package key ID. Select a OS key name from available OS key names. + * *Optional* * `redundant_network` | *boolean* * If `redundant_network` is `true`, two physical network interfaces will be provided with a bonding configuration. * *Default*: False @@ -298,7 +295,7 @@ The following arguments are supported: **Quote based provisioning only attributes** * `quote_id` | *int* - * Create a pre-set configured bare metal server or custom bare metal server using the quote. + * Create a bare metal server using the quote. * If quote_id is defined, the terraform uses specifications in the quote to create a bare metal server. * You can find the quote id by navigating on the portal to _Account > Sales > Quotes_. * *Optional* diff --git a/softlayer/data_source_softlayer_quote_bare_metal.go b/softlayer/data_source_softlayer_quote_bare_metal.go index ea9e8f17f..e04db2e15 100644 --- a/softlayer/data_source_softlayer_quote_bare_metal.go +++ b/softlayer/data_source_softlayer_quote_bare_metal.go @@ -88,13 +88,11 @@ func dataSourceSoftLayerQuoteBareMetal() *schema.Resource { Computed: true, }, - // Custom bare metal server only "redundant_power_supply": { Type: schema.TypeBool, Computed: true, }, - // Custom bare metal server only - Order multiple RAID groups "storage_groups": { Type: schema.TypeList, Computed: true, diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index ef929ec9d..7d013cd43 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -91,7 +91,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { Set: schema.HashString, }, - // Pre-set configured bare metal server. - Mandatory attribute + // Hourly only "fixed_config_preset": { Type: schema.TypeString, Optional: true, @@ -99,7 +99,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, - // Pe-set configured / custom bare metal server - Mandatory attribute + // Hourly only "os_reference_code": { Type: schema.TypeString, Optional: true, @@ -116,7 +116,6 @@ func resourceSoftLayerBareMetal() *schema.Resource { ConflictsWith: []string{"os_reference_code"}, }, - // Pre-set configured / custom bare metal server - Mandatory attribute "datacenter": { Type: schema.TypeString, Optional: true, @@ -124,7 +123,6 @@ func resourceSoftLayerBareMetal() *schema.Resource { Computed: true, }, - // Pre-set configured / custom bare metal server "network_speed": { Type: schema.TypeInt, Optional: true, @@ -132,7 +130,6 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, - // Pre-set configured / custom bare metal server "hourly_billing": { Type: schema.TypeBool, Optional: true, @@ -140,7 +137,6 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, - // Pre-set configured / custom bare metal server "private_network_only": { Type: schema.TypeBool, Optional: true, @@ -148,7 +144,6 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, - // Pre-set configured / custom bare metal server "tcp_monitoring": { Type: schema.TypeBool, Optional: true, @@ -157,7 +152,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, - // Custom bare metal server - Mandatory attribute + // Monthly only "package_key_name": { Type: schema.TypeString, Optional: true, @@ -165,7 +160,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, - // Custom bare metal server - Mandatory attribute + // Monthly only "process_key_name": { Type: schema.TypeString, Optional: true, @@ -173,7 +168,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, - // Custom bare metal server - Mandatory attribute + // Monthly only "os_key_name": { Type: schema.TypeString, Optional: true, @@ -181,7 +176,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, - // Custom bare metal server - Mandatory attribute + // Monthly only "disk_key_names": { Type: schema.TypeList, Optional: true, @@ -190,7 +185,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, - // Custom bare metal server only + // Monthly only "redundant_network": { Type: schema.TypeBool, Optional: true, @@ -198,7 +193,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, - // Custom bare metal server only + // Monthly only "unbonded_network": { Type: schema.TypeBool, Optional: true, @@ -206,7 +201,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { ForceNew: true, }, - // Custom bare metal server only + // Monthly only "public_bandwidth": { Type: schema.TypeInt, Optional: true, @@ -215,7 +210,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, - // Custom bare metal server only + // Monthly only "memory": { Type: schema.TypeInt, Optional: true, @@ -223,14 +218,14 @@ func resourceSoftLayerBareMetal() *schema.Resource { Computed: true, }, - // Custom bare metal server only + // Monthly only "redundant_power_supply": { Type: schema.TypeBool, Optional: true, Computed: true, }, - // Custom bare metal server only - Order multiple RAID groups + // Monthly only "storage_groups": { Type: schema.TypeList, Optional: true, @@ -267,7 +262,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { DiffSuppressFunc: applyOnce, }, - // Quote based ordering/custom bare metal server only + // Quote based provisioning, Monthly "public_vlan_id": { Type: schema.TypeInt, Optional: true, @@ -275,7 +270,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { Computed: true, }, - // Quote based ordering/custom bare metal server only + // Quote based provisioning, Monthly "public_subnet": { Type: schema.TypeString, Optional: true, @@ -283,7 +278,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { Computed: true, }, - // Quote based ordering/custom bare metal server only + // Quote based provisioning, Monthly "private_vlan_id": { Type: schema.TypeInt, Optional: true, @@ -291,7 +286,7 @@ func resourceSoftLayerBareMetal() *schema.Resource { Computed: true, }, - // Quote based ordering/custom bare metal server only + // Quote based provisioning, Monthly "private_subnet": { Type: schema.TypeString, Optional: true, @@ -418,7 +413,7 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) hardware, ) } else if _, ok := d.GetOk("fixed_config_preset"); ok { - // Build a pre-configured bare metal server template using fixed_config_preset. + // Build an hourly bare metal server template using fixed_config_preset. hardware, err = getBareMetalOrderFromResourceData(d, meta) if err != nil { return err @@ -429,8 +424,8 @@ func resourceSoftLayerBareMetalCreate(d *schema.ResourceData, meta interface{}) "Encountered problem trying to get the bare metal order template: %s", err) } } else { - // Build a custom bare metal server template - order, err = getCustomBareMetalOrder(d, meta) + // Build a monthly bare metal server template + order, err = getMonthlyBareMetalOrder(d, meta) if err != nil { return fmt.Errorf( "Encountered problem trying to get the custom bare metal order template: %s", err) @@ -776,11 +771,11 @@ func getItemPriceId(items []datatypes.Product_Item, categoryCode string, keyName fmt.Errorf("Could not find the matching item with categorycode %s and keyName %s. Available item(s) is(are) %s", categoryCode, keyName, availableItems) } -func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatypes.Container_Product_Order, error) { +func getMonthlyBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatypes.Container_Product_Order, error) { sess := meta.(ProviderConfig).SoftLayerSession() - // Validate attributes for custom bare metal server ordering. + // Validate attributes for monthly bare metal server ordering. if d.Get("hourly_billing").(bool) { - return datatypes.Container_Product_Order{}, fmt.Errorf("Custom bare metal server only supports monthly billing.") + return datatypes.Container_Product_Order{}, fmt.Errorf("Monthly bare metal server only supports monthly billing.") } model, ok := d.GetOk("package_key_name") @@ -803,7 +798,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return datatypes.Container_Product_Order{}, err } - // 1. Find a package id using custom bare metal package key name. + // 1. Find a package id using monthly bare metal package key name. pkg, err := getPackageByModel(sess, model.(string)) if err != nil { return datatypes.Container_Product_Order{}, err @@ -948,7 +943,7 @@ func getCustomBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatype return order, nil } -// Set common parameters for quite based, pre-set configured, and custom bare metal server ordering. +// Set common parameters for server ordering. func setCommonBareMetalOrderOptions(d *schema.ResourceData, meta interface{}, order datatypes.Container_Product_Order) (datatypes.Container_Product_Order, error) { public_vlan_id := d.Get("public_vlan_id").(int) From 0d893367efaf583825a4b39379277558d441c51e Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Fri, 14 Jul 2017 01:03:06 -0400 Subject: [PATCH 33/35] Fixed bugs in bare metal server. --- docs/resources/softlayer_bare_metal.md | 5 +- .../data_source_softlayer_quote_bare_metal.go | 179 +++++++++--------- softlayer/resource_softlayer_bare_metal.go | 13 +- .../resource_softlayer_bare_metal_test.go | 2 - 4 files changed, 104 insertions(+), 95 deletions(-) diff --git a/docs/resources/softlayer_bare_metal.md b/docs/resources/softlayer_bare_metal.md index f0457493b..7053d3052 100644 --- a/docs/resources/softlayer_bare_metal.md +++ b/docs/resources/softlayer_bare_metal.md @@ -1,7 +1,7 @@ # `softlayer_bare_metal` Provides a `bare_metal` resource. This allows bare metals to be created, updated and deleted. `softlayer_bare_metal` resource supports both monthly bare metal servers and hourly bare metal servers. - For more detail on bare metal types, refer to the [link](https://www.ibm.com/cloud-computing/bluemix/bare-metal-servers) + For more detail on bare metal seves, refer to the [link](https://www.ibm.com/cloud-computing/bluemix/bare-metal-servers) ## Hourly bare metal server If the `softlayer_bare_metal` resource definition has a `fixed_config_preset` attribute, terraform will create an hourly @@ -48,7 +48,7 @@ resource "softlayer_bare_metal" "hourly-bm1" { ## Monthly bare metal server If the `fixed_config_preset` attribute is not configured, terraform will consider it as a monthly bare metal server resource. It provides options to configure process, memory, network, disk, and RAID. Users also can assign VLANs and subnets for the target monthly bare metal server. To configure the monthly bare -metal server, you need to configure `package_key_name`, `proecss_key_name`, `disk_key_names`, and `os_key_name`. The following example describes a basic configuration +metal server, you need to provide additional attributes such as `package_key_name`, `proecss_key_name`, `disk_key_names`, and `os_key_name`. The following example describes a basic configuration of the monthly bare metal server. ### Example of a monthly bare metal server @@ -220,6 +220,7 @@ The following arguments are supported: * Hourly bare metal server : [Get a complete list of the os reference codes available for hourly bare metal servers](https://api.softlayer.com/rest/v3/SoftLayer_Virtual_Guest_Block_Device_Template_Group/getVhdImportSoftwareDescriptions.json?objectMask=referenceCode) (use your api key as the password). * *Optional* * **Conflicts with** `image_template_id`. + **Monthly / Quote based bare metal server provisioning attributes** * `public_vlan_id` | *int* diff --git a/softlayer/data_source_softlayer_quote_bare_metal.go b/softlayer/data_source_softlayer_quote_bare_metal.go index e04db2e15..51aa24aee 100644 --- a/softlayer/data_source_softlayer_quote_bare_metal.go +++ b/softlayer/data_source_softlayer_quote_bare_metal.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/hashicorp/terraform/helper/schema" + "github.com/softlayer/softlayer-go/filter" "github.com/softlayer/softlayer-go/services" "github.com/softlayer/softlayer-go/sl" "strconv" @@ -132,107 +133,111 @@ func dataSourceSoftLayerQuoteBareMetalRead(d *schema.ResourceData, meta interfac quotes, err := service. Mask("id,name,order[items[storageGroups,item],orderTopLevelItems]"). + Filter(filter.Path("activeQuotes.name").Eq(name).Build()). GetActiveQuotes() if err != nil { return fmt.Errorf("Error looking up quote [%s]: %s", name, err) + } else if len(quotes) == 0 { + return fmt.Errorf("No quote was found with the name '%s'", name) } - for _, quote := range quotes { - if quote.Name != nil && *quote.Name == name { - // Build a bare metal template from the quote. - order, err := services.GetBillingOrderQuoteService(sess). - Id(*quote.Id).GetRecalculatedOrderContainer(nil, sl.Bool(false)) - if err != nil { - return fmt.Errorf( - "Encountered problem trying to get the bare metal order template from quote: %s", err) - } - bmPackage, err := services.GetProductPackageService(sess). - Id(*order.PackageId).GetObject() - if err != nil { - return fmt.Errorf("Unable to find a package name from quote: %s", err) - } - if len(order.StorageGroups) > 0 { - storageGroups := make([]map[string]interface{}, 0, len(order.StorageGroups)) - for _, sg := range order.StorageGroups { - storageGroup := make(map[string]interface{}) - storageGroup["array_type_id"] = *sg.ArrayTypeId - storageGroup["array_size"] = sl.Get(sg.ArraySize, 0) - storageGroup["partition_template_id"] = sl.Get(sg.PartitionTemplateId, 0) - storageGroup["hard_drives"] = sg.HardDrives - storageGroups = append(storageGroups, storageGroup) - } + quote := quotes[0] - d.Set("storage_groups", storageGroups) - } - locationId, err := strconv.Atoi(*order.Location) - if err != nil { - return fmt.Errorf("Location Id should be an integer: %s", *order.Location) - } - dc, err := services.GetLocationDatacenterService(sess).Id(locationId).GetObject() - if err != nil { - return fmt.Errorf("Unable to find a data center from quote: %s", err) + if quote.Name != nil && *quote.Name == name { + // Build a bare metal template from the quote. + order, err := services.GetBillingOrderQuoteService(sess). + Id(*quote.Id).GetRecalculatedOrderContainer(nil, sl.Bool(false)) + if err != nil { + return fmt.Errorf( + "Encountered problem trying to get the bare metal order template from quote: %s", err) + } + bmPackage, err := services.GetProductPackageService(sess). + Id(*order.PackageId).GetObject() + if err != nil { + return fmt.Errorf("Unable to find a package name from quote: %s", err) + } + if len(order.StorageGroups) > 0 { + storageGroups := make([]map[string]interface{}, 0, len(order.StorageGroups)) + for _, sg := range order.StorageGroups { + storageGroup := make(map[string]interface{}) + storageGroup["array_type_id"] = *sg.ArrayTypeId + storageGroup["array_size"] = sl.Get(sg.ArraySize, 0) + storageGroup["partition_template_id"] = sl.Get(sg.PartitionTemplateId, 0) + storageGroup["hard_drives"] = sg.HardDrives + storageGroups = append(storageGroups, storageGroup) } - d.Set("datacenter", *dc.Name) - d.SetId(fmt.Sprintf("%d", *quote.Id)) - d.Set("package_key_name", *bmPackage.KeyName) - d.Set("redundant_power_supply", false) - diskMap := make(map[int]string) - - for _, item := range quote.Order.Items { - switch *item.CategoryCode { - case "server": - d.Set("process_key_name", *item.Item.KeyName) - case "os": - d.Set("os_key_name", *item.Item.KeyName) - case "ram": - d.Set("memory", int(*item.Item.Capacity)) - case "bandwidth": - d.Set("public_bandwidth", int(*item.Item.Capacity)) - case "port_speed": - d.Set("network_speed", int(*item.Item.Capacity)) - d.Set("unbonded_network", false) - d.Set("redundant_network", false) - d.Set("private_network_only", false) - if strings.Contains(*item.Item.KeyName, "UNBONDED") { - d.Set("unbonded_network", true) - } - if strings.Contains(*item.Item.KeyName, "REDUNDANT") { - d.Set("redundant_network", true) - } - if !strings.Contains(*item.Item.KeyName, "PUBLIC") { - d.Set("private_network_only", true) - } - case "power_supply": - d.Set("redundant_power_supply", true) - case "monitoring": - d.Set("tcp_monitoring", false) - if strings.Contains(*item.Item.KeyName, "TCP") { - d.Set("tcp_monitoring", true) - } - } - if strings.HasPrefix(*item.CategoryCode, "disk") { - diskIndex, err := strconv.Atoi(strings.Split(*item.CategoryCode, "disk")[1]) - if err == nil { - diskMap[diskIndex] = *item.Item.KeyName - } + d.Set("storage_groups", storageGroups) + } + locationId, err := strconv.Atoi(*order.Location) + if err != nil { + return fmt.Errorf("Location Id should be an integer: %s", *order.Location) + } + dc, err := services.GetLocationDatacenterService(sess).Id(locationId).GetObject() + if err != nil { + return fmt.Errorf("Unable to find a data center from quote: %s", err) + } + d.Set("datacenter", *dc.Name) + d.SetId(fmt.Sprintf("%d", *quote.Id)) + d.Set("package_key_name", *bmPackage.KeyName) + d.Set("redundant_power_supply", false) + diskMap := make(map[int]string) + + for _, item := range quote.Order.Items { + switch *item.CategoryCode { + case "server": + d.Set("process_key_name", *item.Item.KeyName) + case "os": + d.Set("os_key_name", *item.Item.KeyName) + case "ram": + d.Set("memory", int(*item.Item.Capacity)) + case "bandwidth": + d.Set("public_bandwidth", int(*item.Item.Capacity)) + case "port_speed": + d.Set("network_speed", int(*item.Item.Capacity)) + d.Set("unbonded_network", false) + d.Set("redundant_network", false) + d.Set("private_network_only", false) + if strings.Contains(*item.Item.KeyName, "UNBONDED") { + d.Set("unbonded_network", true) + } + if strings.Contains(*item.Item.KeyName, "REDUNDANT") { + d.Set("redundant_network", true) + } + if !strings.Contains(*item.Item.KeyName, "PUBLIC") { + d.Set("private_network_only", true) } + case "power_supply": + d.Set("redundant_power_supply", true) + case "monitoring": + d.Set("tcp_monitoring", false) + if strings.Contains(*item.Item.KeyName, "TCP") { + d.Set("tcp_monitoring", true) + } + } + if strings.HasPrefix(*item.CategoryCode, "disk") { + diskIndex, err := strconv.Atoi(strings.Split(*item.CategoryCode, "disk")[1]) + if err == nil { + diskMap[diskIndex] = *item.Item.KeyName + } } - numberOfDisk := len(diskMap) - if numberOfDisk > 0 { - disks := make([]string, numberOfDisk, numberOfDisk) - for i := 0; i < numberOfDisk; i++ { - if len(diskMap[i]) > 0 { - disks[i] = diskMap[i] - } else { - return fmt.Errorf("Unable to retrieve disk information.") - } + + } + numberOfDisk := len(diskMap) + if numberOfDisk > 0 { + disks := make([]string, numberOfDisk, numberOfDisk) + for i := 0; i < numberOfDisk; i++ { + if len(diskMap[i]) > 0 { + disks[i] = diskMap[i] + } else { + return fmt.Errorf("Unable to retrieve disk information.") } - d.Set("disk_key_names", disks) } - return nil + d.Set("disk_key_names", disks) } + return nil + } return fmt.Errorf("Could not find quote with name [%s]", name) diff --git a/softlayer/resource_softlayer_bare_metal.go b/softlayer/resource_softlayer_bare_metal.go index 7d013cd43..7db80fd5b 100644 --- a/softlayer/resource_softlayer_bare_metal.go +++ b/softlayer/resource_softlayer_bare_metal.go @@ -683,7 +683,7 @@ func waitForBareMetalProvision(d *datatypes.Hardware, meta interface{}) (interfa } }, Timeout: 24 * time.Hour, - Delay: 60 * time.Second, + Delay: 10 * time.Second, MinTimeout: 1 * time.Minute, NotFoundChecks: 24 * 60, } @@ -711,7 +711,7 @@ func waitForNoBareMetalActiveTransactions(id int, meta interface{}) (interface{} } }, Timeout: 24 * time.Hour, - Delay: 60 * time.Second, + Delay: 10 * time.Second, MinTimeout: 1 * time.Minute, NotFoundChecks: 24 * 60, } @@ -931,14 +931,19 @@ func getMonthlyBareMetalOrder(d *schema.ResourceData, meta interface{}) (datatyp } // Add storage_groups for RAID configuration - diskController, err := getItemPriceId(items, "disk_controller", "DISK_CONTROLLER_RAID") + diskController, err := getItemPriceId(items, "disk_controller", "DISK_CONTROLLER_NONRAID") if err != nil { return datatypes.Container_Product_Order{}, err } - order.Prices = append(order.Prices, diskController) + if _, ok := d.GetOk("storage_groups"); ok { order.StorageGroups = getStorageGroupsFromResourceData(d) + diskController, err = getItemPriceId(items, "disk_controller", "DISK_CONTROLLER_RAID") + if err != nil { + return datatypes.Container_Product_Order{}, err + } } + order.Prices = append(order.Prices, diskController) return order, nil } diff --git a/softlayer/resource_softlayer_bare_metal_test.go b/softlayer/resource_softlayer_bare_metal_test.go index 1310c66d8..7ca601057 100644 --- a/softlayer/resource_softlayer_bare_metal_test.go +++ b/softlayer/resource_softlayer_bare_metal_test.go @@ -85,8 +85,6 @@ func TestAccSoftLayerBareMetalQuote_Basic(t *testing.T) { "softlayer_bare_metal.terraform-acceptance-test-2", "domain", "bar.example.com"), resource.TestCheckResourceAttr( "softlayer_bare_metal.terraform-acceptance-test-2", "user_metadata", "{\"value\":\"newvalue\"}"), - resource.TestCheckResourceAttr( - "softlayer_bare_metal.terraform-acceptance-test-2", "quote_id", "2179879"), CheckStringSet( "softlayer_bare_metal.terraform-acceptance-test-2", "tags", []string{"collectd"}, From f1eaf92c49978fb46574a19092ea237b0292ecb8 Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 17 Jul 2017 14:13:33 -0400 Subject: [PATCH 34/35] Deleted quote_bare_metal data source. --- .../datasources/softlayer_quote_bare_metal.md | 36 --- .../data_source_softlayer_quote_bare_metal.go | 244 ------------------ ..._source_softlayer_quote_bare_metal_test.go | 43 --- softlayer/provider.go | 1 - 4 files changed, 324 deletions(-) delete mode 100644 docs/datasources/softlayer_quote_bare_metal.md delete mode 100644 softlayer/data_source_softlayer_quote_bare_metal.go delete mode 100644 softlayer/data_source_softlayer_quote_bare_metal_test.go diff --git a/docs/datasources/softlayer_quote_bare_metal.md b/docs/datasources/softlayer_quote_bare_metal.md deleted file mode 100644 index 010dd5102..000000000 --- a/docs/datasources/softlayer_quote_bare_metal.md +++ /dev/null @@ -1,36 +0,0 @@ -# `softlayer_quote_bare_metal` - -Use this data source to import the name of an *existing* custom bare metal quote as a read-only data source. - -## Example Usage - -```hcl -data softlayer_quote_bare_metal quote_test{ - name = "quote_test" -} -``` - -It imports the quote of the custom bare metal server and shows detailed attributes. - - -## Argument Reference - -`name` - (Required) The name of the quote, as it was defined in SoftLayer - -## Attributes Reference - -`id` - Set to the ID of the quote. -`datacenter` - It specifies which datacenter the instance is to be provisioned in. -`os_reference_code` - Target OS key name. -`network_speed` - Specifies the connection speed (in Mbps) for the instance's network components. -`private_network_only` - Specifies whether or not the instance only has access to the private network. -`package_key_name` - Custom bare metal server's package key name. -`process_key_name` - Custom bare metal server's process key name. -`disk_key_names` - Array of internal disk key names. -`redundant_network` - If `redundant_network` is `true`, two physical network interfaces will be provided with a bonding configuration. -`unbonded_network` - If `unbonded_network` is `true`, two physical network interfaces will be provided. -`public_bandwidth` - Allowed public network traffic(GB) per month. -`memory` - An amount of memory(GB) for the server. -`storage_groups` - RAID and partition configuration. -`redundant_power_supply`- If `redundant_power_supply` is true, an additional power supply will be provided. -`tcp_monitoring` - If `tcp_monitoring` is `true`, ping and tcp monitoring service will be provided. \ No newline at end of file diff --git a/softlayer/data_source_softlayer_quote_bare_metal.go b/softlayer/data_source_softlayer_quote_bare_metal.go deleted file mode 100644 index 51aa24aee..000000000 --- a/softlayer/data_source_softlayer_quote_bare_metal.go +++ /dev/null @@ -1,244 +0,0 @@ -package softlayer - -import ( - "fmt" - - "github.com/hashicorp/terraform/helper/schema" - "github.com/softlayer/softlayer-go/filter" - "github.com/softlayer/softlayer-go/services" - "github.com/softlayer/softlayer-go/sl" - "strconv" - "strings" -) - -func dataSourceSoftLayerQuoteBareMetal() *schema.Resource { - return &schema.Resource{ - Read: dataSourceSoftLayerQuoteBareMetalRead, - - Schema: map[string]*schema.Schema{ - "id": { - Description: "The internal id of the quote for bare metal server", - Type: schema.TypeInt, - Computed: true, - }, - - "name": { - Description: "The name of this quote", - Type: schema.TypeString, - Required: true, - }, - - "datacenter": { - Type: schema.TypeString, - Computed: true, - }, - - "network_speed": { - Type: schema.TypeInt, - Computed: true, - }, - - "private_network_only": { - Type: schema.TypeBool, - Computed: true, - }, - - "tcp_monitoring": { - Type: schema.TypeBool, - Computed: true, - }, - - "package_key_name": { - Type: schema.TypeString, - Computed: true, - }, - - "process_key_name": { - Type: schema.TypeString, - Computed: true, - }, - - "os_key_name": { - Type: schema.TypeString, - Computed: true, - }, - - "disk_key_names": { - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Computed: true, - }, - - "redundant_network": { - Type: schema.TypeBool, - Computed: true, - }, - - "unbonded_network": { - Type: schema.TypeBool, - Computed: true, - }, - - "public_bandwidth": { - Type: schema.TypeInt, - Computed: true, - }, - - "memory": { - Type: schema.TypeInt, - Computed: true, - }, - - "redundant_power_supply": { - Type: schema.TypeBool, - Computed: true, - }, - - "storage_groups": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "array_type_id": { - Type: schema.TypeInt, - Computed: true, - }, - "hard_drives": { - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeInt}, - Computed: true, - }, - "array_size": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - }, - "partition_template_id": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - }, - }, - }, - }, - }, - } -} - -func dataSourceSoftLayerQuoteBareMetalRead(d *schema.ResourceData, meta interface{}) error { - sess := meta.(ProviderConfig).SoftLayerSession() - service := services.GetAccountService(sess) - - name := d.Get("name").(string) - - quotes, err := service. - Mask("id,name,order[items[storageGroups,item],orderTopLevelItems]"). - Filter(filter.Path("activeQuotes.name").Eq(name).Build()). - GetActiveQuotes() - if err != nil { - return fmt.Errorf("Error looking up quote [%s]: %s", name, err) - } else if len(quotes) == 0 { - return fmt.Errorf("No quote was found with the name '%s'", name) - } - - quote := quotes[0] - - if quote.Name != nil && *quote.Name == name { - // Build a bare metal template from the quote. - order, err := services.GetBillingOrderQuoteService(sess). - Id(*quote.Id).GetRecalculatedOrderContainer(nil, sl.Bool(false)) - if err != nil { - return fmt.Errorf( - "Encountered problem trying to get the bare metal order template from quote: %s", err) - } - bmPackage, err := services.GetProductPackageService(sess). - Id(*order.PackageId).GetObject() - if err != nil { - return fmt.Errorf("Unable to find a package name from quote: %s", err) - } - if len(order.StorageGroups) > 0 { - storageGroups := make([]map[string]interface{}, 0, len(order.StorageGroups)) - for _, sg := range order.StorageGroups { - storageGroup := make(map[string]interface{}) - storageGroup["array_type_id"] = *sg.ArrayTypeId - storageGroup["array_size"] = sl.Get(sg.ArraySize, 0) - storageGroup["partition_template_id"] = sl.Get(sg.PartitionTemplateId, 0) - storageGroup["hard_drives"] = sg.HardDrives - storageGroups = append(storageGroups, storageGroup) - } - - d.Set("storage_groups", storageGroups) - } - locationId, err := strconv.Atoi(*order.Location) - if err != nil { - return fmt.Errorf("Location Id should be an integer: %s", *order.Location) - } - dc, err := services.GetLocationDatacenterService(sess).Id(locationId).GetObject() - if err != nil { - return fmt.Errorf("Unable to find a data center from quote: %s", err) - } - d.Set("datacenter", *dc.Name) - d.SetId(fmt.Sprintf("%d", *quote.Id)) - d.Set("package_key_name", *bmPackage.KeyName) - d.Set("redundant_power_supply", false) - diskMap := make(map[int]string) - - for _, item := range quote.Order.Items { - switch *item.CategoryCode { - case "server": - d.Set("process_key_name", *item.Item.KeyName) - case "os": - d.Set("os_key_name", *item.Item.KeyName) - case "ram": - d.Set("memory", int(*item.Item.Capacity)) - case "bandwidth": - d.Set("public_bandwidth", int(*item.Item.Capacity)) - case "port_speed": - d.Set("network_speed", int(*item.Item.Capacity)) - d.Set("unbonded_network", false) - d.Set("redundant_network", false) - d.Set("private_network_only", false) - if strings.Contains(*item.Item.KeyName, "UNBONDED") { - d.Set("unbonded_network", true) - } - if strings.Contains(*item.Item.KeyName, "REDUNDANT") { - d.Set("redundant_network", true) - } - if !strings.Contains(*item.Item.KeyName, "PUBLIC") { - d.Set("private_network_only", true) - } - case "power_supply": - d.Set("redundant_power_supply", true) - case "monitoring": - d.Set("tcp_monitoring", false) - if strings.Contains(*item.Item.KeyName, "TCP") { - d.Set("tcp_monitoring", true) - } - } - - if strings.HasPrefix(*item.CategoryCode, "disk") { - diskIndex, err := strconv.Atoi(strings.Split(*item.CategoryCode, "disk")[1]) - if err == nil { - diskMap[diskIndex] = *item.Item.KeyName - } - } - - } - numberOfDisk := len(diskMap) - if numberOfDisk > 0 { - disks := make([]string, numberOfDisk, numberOfDisk) - for i := 0; i < numberOfDisk; i++ { - if len(diskMap[i]) > 0 { - disks[i] = diskMap[i] - } else { - return fmt.Errorf("Unable to retrieve disk information.") - } - } - d.Set("disk_key_names", disks) - } - return nil - - } - - return fmt.Errorf("Could not find quote with name [%s]", name) -} diff --git a/softlayer/data_source_softlayer_quote_bare_metal_test.go b/softlayer/data_source_softlayer_quote_bare_metal_test.go deleted file mode 100644 index 7a7b2c943..000000000 --- a/softlayer/data_source_softlayer_quote_bare_metal_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package softlayer - -import ( - "testing" - - "github.com/hashicorp/terraform/helper/resource" -) - -func TestAccSoftLayerQuoteBareMetalDataSource_Basic(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: testAccCheckSoftLayerQuoteBareMetalDataSourceConfig_basic, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "data.softlayer_quote_bare_metal.test_quote_bm", - "package_key_name", - "4U_DUAL_E52600_36_DRIVES", - ), - resource.TestCheckResourceAttr( - "data.softlayer_quote_bare_metal.test_quote_bm", - "process_key_name", - "INTEL_XEON_2650_2_00", - ), - resource.TestCheckResourceAttr( - "data.softlayer_quote_bare_metal.test_quote_bm", - "datacenter", - "dal06", - ), - ), - }, - }, - }) -} - -// The datasource to apply -const testAccCheckSoftLayerQuoteBareMetalDataSourceConfig_basic = ` -data "softlayer_quote_bare_metal" "test_quote_bm" { - name = "test" -} -` diff --git a/softlayer/provider.go b/softlayer/provider.go index b8a5d43b0..a0afb7a4f 100644 --- a/softlayer/provider.go +++ b/softlayer/provider.go @@ -50,7 +50,6 @@ func Provider() terraform.ResourceProvider { "softlayer_image_template": dataSourceSoftLayerImageTemplate(), "softlayer_vlan": dataSourceSoftLayerVlan(), "softlayer_dns_domain": dataSourceSoftLayerDnsDomain(), - "softlayer_quote_bare_metal": dataSourceSoftLayerQuoteBareMetal(), }, ResourcesMap: map[string]*schema.Resource{ From 4deb31447fe6cefc7a7dd7092f0c9bb64d65935f Mon Sep 17 00:00:00 2001 From: Minsik Lee Date: Mon, 17 Jul 2017 14:15:34 -0400 Subject: [PATCH 35/35] Executed make fmt. --- softlayer/provider.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/softlayer/provider.go b/softlayer/provider.go index a0afb7a4f..c331e467a 100644 --- a/softlayer/provider.go +++ b/softlayer/provider.go @@ -46,10 +46,10 @@ func Provider() terraform.ResourceProvider { }, DataSourcesMap: map[string]*schema.Resource{ - "softlayer_ssh_key": dataSourceSoftLayerSSHKey(), - "softlayer_image_template": dataSourceSoftLayerImageTemplate(), - "softlayer_vlan": dataSourceSoftLayerVlan(), - "softlayer_dns_domain": dataSourceSoftLayerDnsDomain(), + "softlayer_ssh_key": dataSourceSoftLayerSSHKey(), + "softlayer_image_template": dataSourceSoftLayerImageTemplate(), + "softlayer_vlan": dataSourceSoftLayerVlan(), + "softlayer_dns_domain": dataSourceSoftLayerDnsDomain(), }, ResourcesMap: map[string]*schema.Resource{