blob: 7171afada43f15cee96b2ee598772522dc68c421 [file] [log] [blame]
Jon Perritt654fb0e2014-10-23 20:54:14 -05001package bootfromvolume
2
3import (
Jon Perrittf094fef2016-03-07 01:41:59 -06004 "fmt"
Jon Perrittd9a4bf72014-10-23 23:44:04 -05005 "strconv"
Jon Perritt654fb0e2014-10-23 20:54:14 -05006
Jon Perritt27249f42016-02-18 10:35:59 -06007 "github.com/gophercloud/gophercloud"
8 "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
Jon Perritt654fb0e2014-10-23 20:54:14 -05009)
10
Jon Perritt01686cd2014-10-24 14:10:16 -050011// SourceType represents the type of medium being used to create the volume.
12type SourceType string
13
14const (
15 Volume SourceType = "volume"
16 Snapshot SourceType = "snapshot"
17 Image SourceType = "image"
Joe Topjianecf63dd2015-12-12 20:33:50 +000018 Blank SourceType = "blank"
Jon Perritt01686cd2014-10-24 14:10:16 -050019)
20
Jon Perrittd9a4bf72014-10-23 23:44:04 -050021// BlockDevice is a structure with options for booting a server instance
22// from a volume. The volume may be created from an image, snapshot, or another
23// volume.
24type BlockDevice struct {
25 // BootIndex [optional] is the boot index. It defaults to 0.
Jon Perritt8dd49db2014-10-24 12:39:07 -050026 BootIndex int `json:"boot_index"`
Jon Perrittd9a4bf72014-10-23 23:44:04 -050027
28 // DeleteOnTermination [optional] specifies whether or not to delete the attached volume
29 // when the server is deleted. Defaults to `false`.
Jon Perritt8dd49db2014-10-24 12:39:07 -050030 DeleteOnTermination bool `json:"delete_on_termination"`
Jon Perrittd9a4bf72014-10-23 23:44:04 -050031
32 // DestinationType [optional] is the type that gets created. Possible values are "volume"
33 // and "local".
Jon Perritt8dd49db2014-10-24 12:39:07 -050034 DestinationType string `json:"destination_type"`
Jon Perrittd9a4bf72014-10-23 23:44:04 -050035
Joe Topjianecf63dd2015-12-12 20:33:50 +000036 // GuestFormat [optional] specifies the format of the block device.
37 GuestFormat string `json:"guest_format"`
38
Jon Perritt61710222014-10-24 13:01:31 -050039 // SourceType [required] must be one of: "volume", "snapshot", "image".
Jon Perritt01686cd2014-10-24 14:10:16 -050040 SourceType SourceType `json:"source_type"`
Jon Perrittd9a4bf72014-10-23 23:44:04 -050041
Jon Perritt61710222014-10-24 13:01:31 -050042 // UUID [required] is the unique identifier for the volume, snapshot, or image (see above)
Jon Perritt8dd49db2014-10-24 12:39:07 -050043 UUID string `json:"uuid"`
Jon Perrittd9a4bf72014-10-23 23:44:04 -050044
45 // VolumeSize [optional] is the size of the volume to create (in gigabytes).
Jon Perritt8dd49db2014-10-24 12:39:07 -050046 VolumeSize int `json:"volume_size"`
Jon Perrittd9a4bf72014-10-23 23:44:04 -050047}
48
49// CreateOptsExt is a structure that extends the server `CreateOpts` structure
50// by allowing for a block device mapping.
Jon Perritt654fb0e2014-10-23 20:54:14 -050051type CreateOptsExt struct {
52 servers.CreateOptsBuilder
Jon Perritt01686cd2014-10-24 14:10:16 -050053 BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"`
Jon Perritt654fb0e2014-10-23 20:54:14 -050054}
55
56// ToServerCreateMap adds the block device mapping option to the base server
57// creation options.
58func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
Jon Perritt8dd49db2014-10-24 12:39:07 -050059 base, err := opts.CreateOptsBuilder.ToServerCreateMap()
60 if err != nil {
61 return nil, err
62 }
63
Jon Perritt01686cd2014-10-24 14:10:16 -050064 if len(opts.BlockDevice) == 0 {
Jon Perrittf094fef2016-03-07 01:41:59 -060065 err := gophercloud.ErrMissingInput{}
66 err.Function = "bootfromvolume.ToServerCreateMap"
67 err.Argument = "bootfromvolume.CreateOptsExt.BlockDevice"
68 return nil, err
Jon Perritt4149d7c2014-10-23 21:23:46 -050069 }
Jon Perritt654fb0e2014-10-23 20:54:14 -050070
Jon Perritt4149d7c2014-10-23 21:23:46 -050071 serverMap := base["server"].(map[string]interface{})
Jon Perrittd9a4bf72014-10-23 23:44:04 -050072
Jon Perritt01686cd2014-10-24 14:10:16 -050073 blockDevice := make([]map[string]interface{}, len(opts.BlockDevice))
Jon Perrittd9a4bf72014-10-23 23:44:04 -050074
Jon Perritt01686cd2014-10-24 14:10:16 -050075 for i, bd := range opts.BlockDevice {
76 if string(bd.SourceType) == "" {
Jon Perrittf094fef2016-03-07 01:41:59 -060077 err := gophercloud.ErrMissingInput{}
78 err.Function = "bootfromvolume.ToServerCreateMap"
79 err.Argument = fmt.Sprintf("bootfromvolume.CreateOptsExt.BlockDevice[%d].SourceType", i)
80 return nil, err
Jon Perritt01686cd2014-10-24 14:10:16 -050081 }
82
83 blockDevice[i] = make(map[string]interface{})
84
85 blockDevice[i]["source_type"] = bd.SourceType
86 blockDevice[i]["boot_index"] = strconv.Itoa(bd.BootIndex)
87 blockDevice[i]["delete_on_termination"] = strconv.FormatBool(bd.DeleteOnTermination)
88 blockDevice[i]["volume_size"] = strconv.Itoa(bd.VolumeSize)
89 if bd.UUID != "" {
90 blockDevice[i]["uuid"] = bd.UUID
91 }
92 if bd.DestinationType != "" {
93 blockDevice[i]["destination_type"] = bd.DestinationType
94 }
Joe Topjianecf63dd2015-12-12 20:33:50 +000095 if bd.GuestFormat != "" {
96 blockDevice[i]["guest_format"] = bd.GuestFormat
97 }
Jon Perritt01686cd2014-10-24 14:10:16 -050098
99 }
100 serverMap["block_device_mapping_v2"] = blockDevice
Jon Perritt654fb0e2014-10-23 20:54:14 -0500101
Jon Perritt4149d7c2014-10-23 21:23:46 -0500102 return base, nil
Jon Perritt654fb0e2014-10-23 20:54:14 -0500103}
Jon Perrittd9a4bf72014-10-23 23:44:04 -0500104
105// Create requests the creation of a server from the given block device mapping.
Jon Perritt01686cd2014-10-24 14:10:16 -0500106func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) servers.CreateResult {
107 var res servers.CreateResult
Jon Perrittd9a4bf72014-10-23 23:44:04 -0500108
109 reqBody, err := opts.ToServerCreateMap()
110 if err != nil {
111 res.Err = err
112 return res
113 }
114
Joe Topjian9d6e6a92015-10-31 23:39:22 +0000115 // Delete imageName and flavorName that come from ToServerCreateMap().
116 // As of Liberty, Boot From Volume is failing if they are passed.
117 delete(reqBody["server"].(map[string]interface{}), "imageName")
118 delete(reqBody["server"].(map[string]interface{}), "flavorName")
119
Jamie Hannaford6a3a78f2015-03-24 14:56:12 +0100120 _, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
121 OkCodes: []int{200, 202},
Jon Perrittd9a4bf72014-10-23 23:44:04 -0500122 })
123 return res
124}