create server helper fields
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests_test.go b/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
index 5bf9137..467f05c 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
@@ -32,6 +32,8 @@
"name": "createdserver",
"imageRef": "asdfasdfasdf",
"flavorRef": "performance1-1",
+ "flavorName": "",
+ "imageName": "",
"block_device_mapping_v2":[
{
"uuid":"123456",
diff --git a/openstack/compute/v2/extensions/diskconfig/requests_test.go b/openstack/compute/v2/extensions/diskconfig/requests_test.go
index e3c26d4..17418a3 100644
--- a/openstack/compute/v2/extensions/diskconfig/requests_test.go
+++ b/openstack/compute/v2/extensions/diskconfig/requests_test.go
@@ -25,6 +25,8 @@
"name": "createdserver",
"imageRef": "asdfasdfasdf",
"flavorRef": "performance1-1",
+ "flavorName": "",
+ "imageName": "",
"OS-DCF:diskConfig": "MANUAL"
}
}
diff --git a/openstack/compute/v2/extensions/schedulerhints/requests_test.go b/openstack/compute/v2/extensions/schedulerhints/requests_test.go
index 491a455..9b38b35 100644
--- a/openstack/compute/v2/extensions/schedulerhints/requests_test.go
+++ b/openstack/compute/v2/extensions/schedulerhints/requests_test.go
@@ -39,7 +39,9 @@
"server": {
"name": "createdserver",
"imageRef": "asdfasdfasdf",
- "flavorRef": "performance1-1"
+ "flavorRef": "performance1-1",
+ "flavorName": "",
+ "imageName": ""
},
"os:scheduler_hints": {
"group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
@@ -97,7 +99,9 @@
"server": {
"name": "createdserver",
"imageRef": "asdfasdfasdf",
- "flavorRef": "performance1-1"
+ "flavorRef": "performance1-1",
+ "flavorName": "",
+ "imageName": ""
},
"os:scheduler_hints": {
"group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go
index 586be67..59123aa 100644
--- a/openstack/compute/v2/flavors/requests.go
+++ b/openstack/compute/v2/flavors/requests.go
@@ -1,6 +1,8 @@
package flavors
import (
+ "fmt"
+
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@@ -66,3 +68,36 @@
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
return res
}
+
+// IDFromName is a convienience function that returns a flavor's ID given its name.
+func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
+ flavorCount := 0
+ flavorID := ""
+ if name == "" {
+ return "", fmt.Errorf("A flavor name must be provided.")
+ }
+ pager := ListDetail(client, nil)
+ pager.EachPage(func(page pagination.Page) (bool, error) {
+ flavorList, err := ExtractFlavors(page)
+ if err != nil {
+ return false, err
+ }
+
+ for _, f := range flavorList {
+ if f.Name == name {
+ flavorCount++
+ flavorID = f.ID
+ }
+ }
+ return true, nil
+ })
+
+ switch flavorCount {
+ case 0:
+ return "", fmt.Errorf("Unable to find flavor: %s", name)
+ case 1:
+ return flavorID, nil
+ default:
+ return "", fmt.Errorf("Found %d flavors matching %s", flavorCount, name)
+ }
+}
diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go
index 7ce5139..1e021ad 100644
--- a/openstack/compute/v2/images/requests.go
+++ b/openstack/compute/v2/images/requests.go
@@ -1,6 +1,8 @@
package images
import (
+ "fmt"
+
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@@ -70,3 +72,38 @@
_, result.Err = client.Delete(deleteURL(client, id), nil)
return result
}
+
+// IDFromName is a convienience function that returns an image's ID given its name.
+func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
+ imageCount := 0
+ imageID := ""
+ if name == "" {
+ return "", fmt.Errorf("An image name must be provided.")
+ }
+ pager := ListDetail(client, &ListOpts{
+ Name: name,
+ })
+ pager.EachPage(func(page pagination.Page) (bool, error) {
+ imageList, err := ExtractImages(page)
+ if err != nil {
+ return false, err
+ }
+
+ for _, i := range imageList {
+ if i.Name == name {
+ imageCount++
+ imageID = i.ID
+ }
+ }
+ return true, nil
+ })
+
+ switch imageCount {
+ case 0:
+ return "", fmt.Errorf("Unable to find image: %s", name)
+ case 1:
+ return imageID, nil
+ default:
+ return "", fmt.Errorf("Found %d images matching %s", imageCount, name)
+ }
+}
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index af77546..48c7df2 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -7,6 +7,8 @@
"fmt"
"github.com/rackspace/gophercloud"
+ "github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
+ "github.com/rackspace/gophercloud/openstack/compute/v2/images"
"github.com/rackspace/gophercloud/pagination"
)
@@ -127,13 +129,24 @@
// Name [required] is the name to assign to the newly launched server.
Name string
- // ImageRef [required] is the ID or full URL to the image that contains the server's OS and initial state.
- // Optional if using the boot-from-volume extension.
+ // ImageRef [optional; required if ImageName is not provided] is the ID or full
+ // URL to the image that contains the server's OS and initial state.
+ // Also optional if using the boot-from-volume extension.
ImageRef string
- // FlavorRef [required] is the ID or full URL to the flavor that describes the server's specs.
+ // ImageName [optional; required if ImageRef is not provided] is the name of the
+ // image that contains the server's OS and initial state.
+ // Also optional if using the boot-from-volume extension.
+ ImageName string
+
+ // FlavorRef [optional; required if FlavorName is not provided] is the ID or
+ // full URL to the flavor that describes the server's specs.
FlavorRef string
+ // FlavorName [optional; required if FlavorRef is not provided] is the name of
+ // the flavor that describes the server's specs.
+ FlavorName string
+
// SecurityGroups [optional] lists the names of the security groups to which this server should belong.
SecurityGroups []string
@@ -175,7 +188,9 @@
server["name"] = opts.Name
server["imageRef"] = opts.ImageRef
+ server["imageName"] = opts.ImageName
server["flavorRef"] = opts.FlavorRef
+ server["flavorName"] = opts.FlavorName
if opts.UserData != nil {
encoded := base64.StdEncoding.EncodeToString(opts.UserData)
@@ -242,6 +257,38 @@
return res
}
+ // If ImageRef isn't provided, use ImageName to ascertain the image ID.
+ if reqBody["server"].(map[string]interface{})["imageRef"].(string) == "" {
+ imageName := reqBody["server"].(map[string]interface{})["imageName"].(string)
+ if imageName == "" {
+ res.Err = errors.New("One and only one of ImageRef and ImageName must be provided.")
+ return res
+ }
+ imageID, err := images.IDFromName(client, imageName)
+ if err != nil {
+ res.Err = err
+ return res
+ }
+ reqBody["server"].(map[string]interface{})["imageRef"] = imageID
+ }
+ delete(reqBody["server"].(map[string]interface{}), "imageName")
+
+ // If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID.
+ if reqBody["server"].(map[string]interface{})["flavorRef"].(string) == "" {
+ flavorName := reqBody["server"].(map[string]interface{})["flavorName"].(string)
+ if flavorName == "" {
+ res.Err = errors.New("One and only one of FlavorRef and FlavorName must be provided.")
+ return res
+ }
+ flavorID, err := flavors.IDFromName(client, flavorName)
+ if err != nil {
+ res.Err = err
+ return res
+ }
+ reqBody["server"].(map[string]interface{})["flavorRef"] = flavorID
+ }
+ delete(reqBody["server"].(map[string]interface{}), "flavorName")
+
_, res.Err = client.Post(listURL(client), reqBody, &res.Body, nil)
return res
}