@@ -191,17 +191,17 @@ func resourceComputeInstanceV2() *schema.Resource {
191191 ForceNew : true ,
192192 Elem : & schema.Resource {
193193 Schema : map [string ]* schema.Schema {
194- "uuid " : & schema.Schema {
194+ "source_type " : & schema.Schema {
195195 Type : schema .TypeString ,
196196 Required : true ,
197197 },
198- "source_type " : & schema.Schema {
198+ "uuid " : & schema.Schema {
199199 Type : schema .TypeString ,
200- Required : true ,
200+ Optional : true ,
201201 },
202202 "volume_size" : & schema.Schema {
203203 Type : schema .TypeInt ,
204- Required : true ,
204+ Optional : true ,
205205 },
206206 "destination_type" : & schema.Schema {
207207 Type : schema .TypeString ,
@@ -216,6 +216,10 @@ func resourceComputeInstanceV2() *schema.Resource {
216216 Optional : true ,
217217 Default : false ,
218218 },
219+ "guest_format" : & schema.Schema {
220+ Type : schema .TypeString ,
221+ Optional : true ,
222+ },
219223 },
220224 },
221225 },
@@ -330,13 +334,18 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
330334 return err
331335 }
332336
333- // determine if volume/block_device configuration is correct
337+ // determine if volume configuration is correct
334338 // this includes ensuring volume_ids are set
335- // and if only one block_device was specified.
336339 if err := checkVolumeConfig (d ); err != nil {
337340 return err
338341 }
339342
343+ // determine if block_device configuration is correct
344+ // this includes valid combinations and required attributes
345+ if err := checkBlockDeviceConfig (d ); err != nil {
346+ return err
347+ }
348+
340349 // check if floating IP configuration is correct
341350 if err := checkInstanceFloatingIPs (d ); err != nil {
342351 return err
@@ -380,14 +389,10 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
380389 }
381390
382391 if vL , ok := d .GetOk ("block_device" ); ok {
383- for _ , v := range vL .([]interface {}) {
384- blockDeviceRaw := v .(map [string ]interface {})
385- blockDevice := resourceInstanceBlockDeviceV2 (d , blockDeviceRaw )
386- createOpts = & bootfromvolume.CreateOptsExt {
387- CreateOptsBuilder : createOpts ,
388- BlockDevice : blockDevice ,
389- }
390- log .Printf ("[DEBUG] Create BFV Options: %+v" , createOpts )
392+ blockDevices := resourceInstanceBlockDevicesV2 (d , vL .([]interface {}))
393+ createOpts = & bootfromvolume.CreateOptsExt {
394+ createOpts ,
395+ blockDevices ,
391396 }
392397 }
393398
@@ -1091,20 +1096,24 @@ func resourceInstanceMetadataV2(d *schema.ResourceData) map[string]string {
10911096 return m
10921097}
10931098
1094- func resourceInstanceBlockDeviceV2 (d * schema.ResourceData , bd map [string ]interface {}) []bootfromvolume.BlockDevice {
1095- sourceType := bootfromvolume .SourceType (bd ["source_type" ].(string ))
1096- bfvOpts := []bootfromvolume.BlockDevice {
1097- bootfromvolume.BlockDevice {
1098- UUID : bd ["uuid" ].(string ),
1099+ func resourceInstanceBlockDevicesV2 (d * schema.ResourceData , bds []interface {}) []bootfromvolume.BlockDevice {
1100+ blockDeviceOpts := make ([]bootfromvolume.BlockDevice , len (bds ))
1101+ for i , bd := range bds {
1102+ bdM := bd .(map [string ]interface {})
1103+ sourceType := bootfromvolume .SourceType (bdM ["source_type" ].(string ))
1104+ blockDeviceOpts [i ] = bootfromvolume.BlockDevice {
1105+ UUID : bdM ["uuid" ].(string ),
10991106 SourceType : sourceType ,
1100- VolumeSize : bd ["volume_size" ].(int ),
1101- DestinationType : bd ["destination_type" ].(string ),
1102- BootIndex : bd ["boot_index" ].(int ),
1103- DeleteOnTermination : bd ["delete_on_termination" ].(bool ),
1104- },
1107+ VolumeSize : bdM ["volume_size" ].(int ),
1108+ DestinationType : bdM ["destination_type" ].(string ),
1109+ BootIndex : bdM ["boot_index" ].(int ),
1110+ DeleteOnTermination : bdM ["delete_on_termination" ].(bool ),
1111+ GuestFormat : bdM ["guest_format" ].(string ),
1112+ }
11051113 }
11061114
1107- return bfvOpts
1115+ log .Printf ("[DEBUG] Block Device Options: %+v" , blockDeviceOpts )
1116+ return blockDeviceOpts
11081117}
11091118
11101119func resourceInstanceSchedulerHintsV2 (d * schema.ResourceData , schedulerHintsRaw map [string ]interface {}) schedulerhints.SchedulerHints {
@@ -1142,10 +1151,19 @@ func resourceInstanceSchedulerHintsV2(d *schema.ResourceData, schedulerHintsRaw
11421151}
11431152
11441153func getImageIDFromConfig (computeClient * gophercloud.ServiceClient , d * schema.ResourceData ) (string , error ) {
1145- // If block_device was used, an Image does not need to be specified.
1146- // If an Image was specified, ignore it
1147- if _ , ok := d .GetOk ("block_device" ); ok {
1148- return "" , nil
1154+ // If block_device was used, an Image does not need to be specified, unless an image/local
1155+ // combination was used. This emulates normal boot behavior. Otherwise, ignore the image altogether.
1156+ if vL , ok := d .GetOk ("block_device" ); ok {
1157+ needImage := false
1158+ for _ , v := range vL .([]interface {}) {
1159+ vM := v .(map [string ]interface {})
1160+ if vM ["source_type" ] == "image" && vM ["destination_type" ] == "local" {
1161+ needImage = true
1162+ }
1163+ }
1164+ if ! needImage {
1165+ return "" , nil
1166+ }
11491167 }
11501168
11511169 if imageId := d .Get ("image_id" ).(string ); imageId != "" {
@@ -1177,11 +1195,20 @@ func getImageIDFromConfig(computeClient *gophercloud.ServiceClient, d *schema.Re
11771195}
11781196
11791197func setImageInformation (computeClient * gophercloud.ServiceClient , server * servers.Server , d * schema.ResourceData ) error {
1180- // If block_device was used, an Image does not need to be specified.
1181- // If an Image was specified, ignore it
1182- if _ , ok := d .GetOk ("block_device" ); ok {
1183- d .Set ("image_id" , "Attempt to boot from volume - no image supplied" )
1184- return nil
1198+ // If block_device was used, an Image does not need to be specified, unless an image/local
1199+ // combination was used. This emulates normal boot behavior. Otherwise, ignore the image altogether.
1200+ if vL , ok := d .GetOk ("block_device" ); ok {
1201+ needImage := false
1202+ for _ , v := range vL .([]interface {}) {
1203+ vM := v .(map [string ]interface {})
1204+ if vM ["source_type" ] == "image" && vM ["destination_type" ] == "local" {
1205+ needImage = true
1206+ }
1207+ }
1208+ if ! needImage {
1209+ d .Set ("image_id" , "Attempt to boot from volume - no image supplied" )
1210+ return nil
1211+ }
11851212 }
11861213
11871214 imageId := server .Image ["id" ].(string )
@@ -1394,9 +1421,29 @@ func checkVolumeConfig(d *schema.ResourceData) error {
13941421 }
13951422 }
13961423
1424+ return nil
1425+ }
1426+
1427+ func checkBlockDeviceConfig (d * schema.ResourceData ) error {
13971428 if vL , ok := d .GetOk ("block_device" ); ok {
1398- if len (vL .([]interface {})) > 1 {
1399- return fmt .Errorf ("Can only specify one block device to boot from." )
1429+ for _ , v := range vL .([]interface {}) {
1430+ vM := v .(map [string ]interface {})
1431+
1432+ if vM ["source_type" ] != "blank" && vM ["uuid" ] == "" {
1433+ return fmt .Errorf ("You must specify a uuid for %s block device types" , vM ["source_type" ])
1434+ }
1435+
1436+ if vM ["source_type" ] == "image" && vM ["destination_type" ] == "volume" {
1437+ if vM ["volume_size" ] == 0 {
1438+ return fmt .Errorf ("You must specify a volume_size when creating a volume from an image" )
1439+ }
1440+ }
1441+
1442+ if vM ["source_type" ] == "blank" && vM ["destination_type" ] == "local" {
1443+ if vM ["volume_size" ] == 0 {
1444+ return fmt .Errorf ("You must specify a volume_size when creating a blank block device" )
1445+ }
1446+ }
14001447 }
14011448 }
14021449
0 commit comments