Added scheduler_hints to the OpenStack instance resource.

This allows various hints to be passed to the OpenStack scheduler
that will determine where the instance will be hosted in the cloud.
This commit is contained in:
Joe Topjian 2015-05-05 20:52:46 +00:00
parent b74e74fc16
commit 7ca7eeabe7
3 changed files with 183 additions and 1 deletions

View File

@ -15,6 +15,7 @@ import (
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
@ -225,6 +226,48 @@ func resourceComputeInstanceV2() *schema.Resource {
},
Set: resourceComputeVolumeAttachmentHash,
},
"scheduler_hints": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"group": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"different_host": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"same_host": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"query": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"target_cell": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"build_near_host_ip": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
},
Set: resourceComputeSchedulerHintsHash,
},
},
}
}
@ -290,6 +333,16 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
}
}
schedulerHintsRaw := d.Get("scheduler_hints").(*schema.Set).List()
if len(schedulerHintsRaw) > 0 {
log.Printf("[DEBUG] schedulerhints: %+v", schedulerHintsRaw)
schedulerHints := resourceInstanceSchedulerHintsV2(d, schedulerHintsRaw[0].(map[string]interface{}))
createOpts = &schedulerhints.CreateOptsExt{
createOpts,
schedulerHints,
}
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
server, err := servers.Create(computeClient, createOpts).Extract()
if err != nil {
@ -873,6 +926,40 @@ func resourceInstanceBlockDeviceV2(d *schema.ResourceData, bd map[string]interfa
return bfvOpts
}
func resourceInstanceSchedulerHintsV2(d *schema.ResourceData, schedulerHintsRaw map[string]interface{}) schedulerhints.SchedulerHints {
differentHost := []string{}
if len(schedulerHintsRaw["different_host"].([]interface{})) > 0 {
for _, dh := range schedulerHintsRaw["different_host"].([]interface{}) {
differentHost = append(differentHost, dh.(string))
}
}
sameHost := []string{}
if len(schedulerHintsRaw["same_host"].([]interface{})) > 0 {
for _, sh := range schedulerHintsRaw["same_host"].([]interface{}) {
sameHost = append(sameHost, sh.(string))
}
}
query := make([]interface{}, len(schedulerHintsRaw["query"].([]interface{})))
if len(schedulerHintsRaw["query"].([]interface{})) > 0 {
for _, q := range schedulerHintsRaw["query"].([]interface{}) {
query = append(query, q.(string))
}
}
schedulerHints := schedulerhints.SchedulerHints{
Group: schedulerHintsRaw["group"].(string),
DifferentHost: differentHost,
SameHost: sameHost,
Query: query,
TargetCell: schedulerHintsRaw["target_cell"].(string),
BuildNearHostIP: schedulerHintsRaw["build_near_host_ip"].(string),
}
return schedulerHints
}
func getImageID(client *gophercloud.ServiceClient, d *schema.ResourceData) (string, error) {
imageId := d.Get("image_id").(string)
@ -959,6 +1046,29 @@ func resourceComputeVolumeAttachmentHash(v interface{}) int {
return hashcode.String(buf.String())
}
func resourceComputeSchedulerHintsHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
if m["group"] != nil {
buf.WriteString(fmt.Sprintf("%s-", m["group"].(string)))
}
if m["target_cell"] != nil {
buf.WriteString(fmt.Sprintf("%s-", m["target_cell"].(string)))
}
if m["build_host_near_ip"] != nil {
buf.WriteString(fmt.Sprintf("%s-", m["build_host_near_ip"].(string)))
}
buf.WriteString(fmt.Sprintf("%s-", m["different_host"].([]interface{})))
buf.WriteString(fmt.Sprintf("%s-", m["same_host"].([]interface{})))
buf.WriteString(fmt.Sprintf("%s-", m["query"].([]interface{})))
return hashcode.String(buf.String())
}
func attachVolumesToInstance(computeClient *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, serverId string, vols []interface{}) error {
if len(vols) > 0 {
for _, v := range vols {

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/terraform/terraform"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
func TestAccComputeV2ServerGroup_basic(t *testing.T) {
@ -28,6 +29,27 @@ func TestAccComputeV2ServerGroup_basic(t *testing.T) {
})
}
func TestAccComputeV2ServerGroup_affinity(t *testing.T) {
var instance servers.Server
var sg servergroups.ServerGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeV2ServerGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeV2ServerGroup_affinity,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeV2ServerGroupExists(t, "openstack_compute_servergroup_v2.mysg", &sg),
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.myinstance", &instance),
testAccCheckComputeV2InstanceInServerGroup(&instance, &sg),
),
},
},
})
}
func testAccCheckComputeV2ServerGroupDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
computeClient, err := config.computeV2Client(OS_REGION_NAME)
@ -81,8 +103,36 @@ func testAccCheckComputeV2ServerGroupExists(t *testing.T, n string, kp *servergr
}
}
func testAccCheckComputeV2InstanceInServerGroup(instance *servers.Server, sg *servergroups.ServerGroup) resource.TestCheckFunc {
return func(s *terraform.State) error {
if len(sg.Members) > 0 {
for _, m := range sg.Members {
if m == instance.ID {
return nil
}
}
}
return fmt.Errorf("Instance %s is not part of Server Group %s", instance.ID, sg.ID)
}
}
var testAccComputeV2ServerGroup_basic = `
resource "openstack_compute_servergroup_v2" "mysg" {
name = "my server group"
name = "mysg"
policies = ["affinity"]
}`
var testAccComputeV2ServerGroup_affinity = `
resource "openstack_compute_servergroup_v2" "mysg" {
name = "mysg"
policies = ["affinity"]
}
resource "openstack_compute_instance_v2" "myinstance" {
name = "myinstance"
security_groups = ["default"]
scheduler_hints {
group = "${openstack_compute_servergroup_v2.mysg.id}"
}
}`

View File

@ -83,6 +83,9 @@ The following arguments are supported:
* `volume` - (Optional) Attach an existing volume to the instance. The volume
structure is described below.
* `scheduler_hints` - (Optional) Provider the Nova scheduler with hints on how
the instance should be launched. The available hints are described below.
The `network` block supports:
* `uuid` - (Required unless `port` or `name` is provided) The network UUID to
@ -119,6 +122,25 @@ The `volume` block supports:
example: `/dev/vdc`. Omit this option to allow the volume to be
auto-assigned a device.
The `scheduler_hints` block supports:
* `group` - (Optional) A UUID of a Server Group. The instance will be placed
into that group.
* `different_host` - (Optional) A list of instance UUIDs. The instance will
be scheduled on a different host than all other instances.
* `same_host` - (Optional) A list of instance UUIDs. The instance will be
scheduled on the same host of those specified.
* `query` - (Optional) A conditional query that a compute node must pass in
order to host an instance.
* `target_cell` - (Optional) The name of a cell to host the instance.
* `build_near_host_ip` - (Optional) An IP Address in CIDR form. The instance
will be placed on a compute node that is in the same subnet.
## Attributes Reference
The following attributes are exported: