multi ephemeral support

This commit adds the ability to specify multiple ephemeral disks through the
bootfromvolume extension.
diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go
index add0e5f..bbecfad 100644
--- a/acceptance/openstack/compute/v2/bootfromvolume_test.go
+++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go
@@ -53,3 +53,64 @@
 	defer servers.Delete(client, server.ID)
 	t.Logf("Deleting server [%s]...", name)
 }
+
+func TestMultiEphemeral(t *testing.T) {
+	client, err := newClient()
+	th.AssertNoErr(t, err)
+
+	if testing.Short() {
+		t.Skip("Skipping test that requires server creation in short mode.")
+	}
+
+	choices, err := ComputeChoicesFromEnv()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	name := tools.RandomString("Gophercloud-", 8)
+	t.Logf("Creating server [%s].", name)
+
+	bd := []bootfromvolume.BlockDevice{
+		bootfromvolume.BlockDevice{
+			BootIndex:           0,
+			UUID:                choices.ImageID,
+			SourceType:          bootfromvolume.Image,
+			DestinationType:     "local",
+			DeleteOnTermination: true,
+		},
+		bootfromvolume.BlockDevice{
+			BootIndex:           -1,
+			SourceType:          bootfromvolume.Blank,
+			DestinationType:     "local",
+			DeleteOnTermination: true,
+			GuestFormat:         "ext4",
+			VolumeSize:          1,
+		},
+		bootfromvolume.BlockDevice{
+			BootIndex:           -1,
+			SourceType:          bootfromvolume.Blank,
+			DestinationType:     "local",
+			DeleteOnTermination: true,
+			GuestFormat:         "ext4",
+			VolumeSize:          1,
+		},
+	}
+
+	serverCreateOpts := servers.CreateOpts{
+		Name:      name,
+		FlavorRef: choices.FlavorID,
+		ImageRef:  choices.ImageID,
+	}
+	server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
+		serverCreateOpts,
+		bd,
+	}).Extract()
+	th.AssertNoErr(t, err)
+	if err = waitForStatus(client, server, "ACTIVE"); err != nil {
+		t.Fatal(err)
+	}
+
+	t.Logf("Created server: %+v\n", server)
+	defer servers.Delete(client, server.ID)
+	t.Logf("Deleting server [%s]...", name)
+}
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go
index c8edee0..dceff3d 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/requests.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go
@@ -15,6 +15,7 @@
 	Volume   SourceType = "volume"
 	Snapshot SourceType = "snapshot"
 	Image    SourceType = "image"
+	Blank    SourceType = "blank"
 )
 
 // BlockDevice is a structure with options for booting a server instance
@@ -32,6 +33,9 @@
 	// and "local".
 	DestinationType string `json:"destination_type"`
 
+	// GuestFormat [optional] specifies the format of the block device.
+	GuestFormat string `json:"guest_format"`
+
 	// SourceType [required] must be one of: "volume", "snapshot", "image".
 	SourceType SourceType `json:"source_type"`
 
@@ -82,6 +86,9 @@
 		if bd.DestinationType != "" {
 			blockDevice[i]["destination_type"] = bd.DestinationType
 		}
+		if bd.GuestFormat != "" {
+			blockDevice[i]["guest_format"] = bd.GuestFormat
+		}
 
 	}
 	serverMap["block_device_mapping_v2"] = blockDevice
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests_test.go b/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
index 8a7fa74..2c8bf49 100644
--- a/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
@@ -32,8 +32,8 @@
         "name": "createdserver",
         "imageRef": "asdfasdfasdf",
         "flavorRef": "performance1-1",
-	"flavorName": "",
-	"imageName": "",
+        "flavorName": "",
+        "imageName": "",
         "block_device_mapping_v2":[
           {
             "uuid":"123456",
@@ -51,3 +51,81 @@
 	th.AssertNoErr(t, err)
 	th.CheckJSONEquals(t, expected, actual)
 }
+
+func TestCreateMultiEphemeralOpts(t *testing.T) {
+	base := servers.CreateOpts{
+		Name:      "createdserver",
+		ImageRef:  "asdfasdfasdf",
+		FlavorRef: "performance1-1",
+	}
+
+	ext := CreateOptsExt{
+		CreateOptsBuilder: base,
+		BlockDevice: []BlockDevice{
+			BlockDevice{
+				BootIndex:           0,
+				DeleteOnTermination: true,
+				DestinationType:     "local",
+				SourceType:          Image,
+				UUID:                "123456",
+			},
+			BlockDevice{
+				BootIndex:           -1,
+				DeleteOnTermination: true,
+				DestinationType:     "local",
+				GuestFormat:         "ext4",
+				SourceType:          Blank,
+				VolumeSize:          1,
+			},
+			BlockDevice{
+				BootIndex:           -1,
+				DeleteOnTermination: true,
+				DestinationType:     "local",
+				GuestFormat:         "ext4",
+				SourceType:          Blank,
+				VolumeSize:          1,
+			},
+		},
+	}
+
+	expected := `
+    {
+      "server": {
+        "name": "createdserver",
+        "imageRef": "asdfasdfasdf",
+        "flavorRef": "performance1-1",
+        "flavorName": "",
+        "imageName": "",
+        "block_device_mapping_v2":[
+          {
+            "boot_index": "0",
+            "delete_on_termination": "true",
+            "destination_type":"local",
+            "source_type":"image",
+            "uuid":"123456",
+            "volume_size": "0"
+          },
+          {
+            "boot_index": "-1",
+            "delete_on_termination": "true",
+            "destination_type":"local",
+            "guest_format":"ext4",
+            "source_type":"blank",
+            "volume_size": "1"
+          },
+          {
+            "boot_index": "-1",
+            "delete_on_termination": "true",
+            "destination_type":"local",
+            "guest_format":"ext4",
+            "source_type":"blank",
+            "volume_size": "1"
+          }
+        ]
+      }
+    }
+  `
+	actual, err := ext.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
+}