| Samuel A. Falvo II | c007c27 | 2014-02-10 20:49:26 -0800 | [diff] [blame] | 1 | package servers | 
|  | 2 |  | 
|  | 3 | import ( | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 4 | "encoding/base64" | 
| Jon Perritt | cc77da6 | 2014-11-16 13:14:21 -0700 | [diff] [blame] | 5 | "errors" | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 6 | "fmt" | 
| Ash Wilson | 01626a3 | 2014-09-17 10:38:07 -0400 | [diff] [blame] | 7 |  | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 8 | "github.com/racker/perigee" | 
| Ash Wilson | 01626a3 | 2014-09-17 10:38:07 -0400 | [diff] [blame] | 9 | "github.com/rackspace/gophercloud" | 
|  | 10 | "github.com/rackspace/gophercloud/pagination" | 
| Samuel A. Falvo II | c007c27 | 2014-02-10 20:49:26 -0800 | [diff] [blame] | 11 | ) | 
|  | 12 |  | 
| Jamie Hannaford | bfe33b2 | 2014-10-16 12:45:40 +0200 | [diff] [blame] | 13 | // ListOptsBuilder allows extensions to add additional parameters to the | 
|  | 14 | // List request. | 
|  | 15 | type ListOptsBuilder interface { | 
|  | 16 | ToServerListQuery() (string, error) | 
|  | 17 | } | 
|  | 18 |  | 
|  | 19 | // ListOpts allows the filtering and sorting of paginated collections through | 
|  | 20 | // the API. Filtering is achieved by passing in struct field values that map to | 
|  | 21 | // the server attributes you want to see returned. Marker and Limit are used | 
|  | 22 | // for pagination. | 
|  | 23 | type ListOpts struct { | 
|  | 24 | // A time/date stamp for when the server last changed status. | 
|  | 25 | ChangesSince string `q:"changes-since"` | 
|  | 26 |  | 
|  | 27 | // Name of the image in URL format. | 
|  | 28 | Image string `q:"image"` | 
|  | 29 |  | 
|  | 30 | // Name of the flavor in URL format. | 
|  | 31 | Flavor string `q:"flavor"` | 
|  | 32 |  | 
|  | 33 | // Name of the server as a string; can be queried with regular expressions. | 
|  | 34 | // Realize that ?name=bob returns both bob and bobb. If you need to match bob | 
|  | 35 | // only, you can use a regular expression matching the syntax of the | 
|  | 36 | // underlying database server implemented for Compute. | 
|  | 37 | Name string `q:"name"` | 
|  | 38 |  | 
|  | 39 | // Value of the status of the server so that you can filter on "ACTIVE" for example. | 
|  | 40 | Status string `q:"status"` | 
|  | 41 |  | 
|  | 42 | // Name of the host as a string. | 
|  | 43 | Host string `q:"host"` | 
|  | 44 |  | 
|  | 45 | // UUID of the server at which you want to set a marker. | 
|  | 46 | Marker string `q:"marker"` | 
|  | 47 |  | 
|  | 48 | // Integer value for the limit of values to return. | 
|  | 49 | Limit int `q:"limit"` | 
|  | 50 | } | 
|  | 51 |  | 
|  | 52 | // ToServerListQuery formats a ListOpts into a query string. | 
|  | 53 | func (opts ListOpts) ToServerListQuery() (string, error) { | 
|  | 54 | q, err := gophercloud.BuildQueryString(opts) | 
|  | 55 | if err != nil { | 
|  | 56 | return "", err | 
|  | 57 | } | 
|  | 58 | return q.String(), nil | 
|  | 59 | } | 
|  | 60 |  | 
| Samuel A. Falvo II | c007c27 | 2014-02-10 20:49:26 -0800 | [diff] [blame] | 61 | // List makes a request against the API to list servers accessible to you. | 
| Jamie Hannaford | bfe33b2 | 2014-10-16 12:45:40 +0200 | [diff] [blame] | 62 | func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { | 
|  | 63 | url := listDetailURL(client) | 
|  | 64 |  | 
|  | 65 | if opts != nil { | 
|  | 66 | query, err := opts.ToServerListQuery() | 
|  | 67 | if err != nil { | 
|  | 68 | return pagination.Pager{Err: err} | 
|  | 69 | } | 
|  | 70 | url += query | 
|  | 71 | } | 
|  | 72 |  | 
| Ash Wilson | b8b16f8 | 2014-10-20 10:19:49 -0400 | [diff] [blame] | 73 | createPageFn := func(r pagination.PageResult) pagination.Page { | 
|  | 74 | return ServerPage{pagination.LinkedPageBase{PageResult: r}} | 
| Samuel A. Falvo II | c007c27 | 2014-02-10 20:49:26 -0800 | [diff] [blame] | 75 | } | 
|  | 76 |  | 
| Jamie Hannaford | bfe33b2 | 2014-10-16 12:45:40 +0200 | [diff] [blame] | 77 | return pagination.NewPager(client, url, createPageFn) | 
| Samuel A. Falvo II | c007c27 | 2014-02-10 20:49:26 -0800 | [diff] [blame] | 78 | } | 
|  | 79 |  | 
| Ash Wilson | 2206a11 | 2014-10-02 10:57:38 -0400 | [diff] [blame] | 80 | // CreateOptsBuilder describes struct types that can be accepted by the Create call. | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 81 | // The CreateOpts struct in this package does. | 
| Ash Wilson | 2206a11 | 2014-10-02 10:57:38 -0400 | [diff] [blame] | 82 | type CreateOptsBuilder interface { | 
| Jon Perritt | 4149d7c | 2014-10-23 21:23:46 -0500 | [diff] [blame] | 83 | ToServerCreateMap() (map[string]interface{}, error) | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 84 | } | 
|  | 85 |  | 
|  | 86 | // Network is used within CreateOpts to control a new server's network attachments. | 
|  | 87 | type Network struct { | 
|  | 88 | // UUID of a nova-network to attach to the newly provisioned server. | 
|  | 89 | // Required unless Port is provided. | 
|  | 90 | UUID string | 
|  | 91 |  | 
|  | 92 | // Port of a neutron network to attach to the newly provisioned server. | 
|  | 93 | // Required unless UUID is provided. | 
|  | 94 | Port string | 
|  | 95 |  | 
|  | 96 | // FixedIP [optional] specifies a fixed IPv4 address to be used on this network. | 
|  | 97 | FixedIP string | 
|  | 98 | } | 
|  | 99 |  | 
|  | 100 | // CreateOpts specifies server creation parameters. | 
|  | 101 | type CreateOpts struct { | 
|  | 102 | // Name [required] is the name to assign to the newly launched server. | 
|  | 103 | Name string | 
|  | 104 |  | 
|  | 105 | // ImageRef [required] is the ID or full URL to the image that contains the server's OS and initial state. | 
|  | 106 | // Optional if using the boot-from-volume extension. | 
|  | 107 | ImageRef string | 
|  | 108 |  | 
|  | 109 | // FlavorRef [required] is the ID or full URL to the flavor that describes the server's specs. | 
|  | 110 | FlavorRef string | 
|  | 111 |  | 
|  | 112 | // SecurityGroups [optional] lists the names of the security groups to which this server should belong. | 
|  | 113 | SecurityGroups []string | 
|  | 114 |  | 
|  | 115 | // UserData [optional] contains configuration information or scripts to use upon launch. | 
|  | 116 | // Create will base64-encode it for you. | 
|  | 117 | UserData []byte | 
|  | 118 |  | 
|  | 119 | // AvailabilityZone [optional] in which to launch the server. | 
|  | 120 | AvailabilityZone string | 
|  | 121 |  | 
|  | 122 | // Networks [optional] dictates how this server will be attached to available networks. | 
|  | 123 | // By default, the server will be attached to all isolated networks for the tenant. | 
|  | 124 | Networks []Network | 
|  | 125 |  | 
|  | 126 | // Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server. | 
|  | 127 | Metadata map[string]string | 
|  | 128 |  | 
|  | 129 | // Personality [optional] includes the path and contents of a file to inject into the server at launch. | 
|  | 130 | // The maximum size of the file is 255 bytes (decoded). | 
|  | 131 | Personality []byte | 
|  | 132 |  | 
|  | 133 | // ConfigDrive [optional] enables metadata injection through a configuration drive. | 
|  | 134 | ConfigDrive bool | 
| Jon Perritt | f3b2e14 | 2014-11-04 16:00:19 -0600 | [diff] [blame] | 135 |  | 
|  | 136 | // AdminPass [optional] sets the root user password. If not set, a randomly-generated | 
|  | 137 | // password will be created and returned in the response. | 
|  | 138 | AdminPass string | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 139 | } | 
|  | 140 |  | 
| Ash Wilson | e45c973 | 2014-09-29 10:54:12 -0400 | [diff] [blame] | 141 | // ToServerCreateMap assembles a request body based on the contents of a CreateOpts. | 
| Jon Perritt | 4149d7c | 2014-10-23 21:23:46 -0500 | [diff] [blame] | 142 | func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 143 | server := make(map[string]interface{}) | 
|  | 144 |  | 
|  | 145 | server["name"] = opts.Name | 
|  | 146 | server["imageRef"] = opts.ImageRef | 
|  | 147 | server["flavorRef"] = opts.FlavorRef | 
|  | 148 |  | 
|  | 149 | if opts.UserData != nil { | 
|  | 150 | encoded := base64.StdEncoding.EncodeToString(opts.UserData) | 
|  | 151 | server["user_data"] = &encoded | 
|  | 152 | } | 
|  | 153 | if opts.Personality != nil { | 
|  | 154 | encoded := base64.StdEncoding.EncodeToString(opts.Personality) | 
|  | 155 | server["personality"] = &encoded | 
|  | 156 | } | 
|  | 157 | if opts.ConfigDrive { | 
|  | 158 | server["config_drive"] = "true" | 
|  | 159 | } | 
|  | 160 | if opts.AvailabilityZone != "" { | 
|  | 161 | server["availability_zone"] = opts.AvailabilityZone | 
|  | 162 | } | 
|  | 163 | if opts.Metadata != nil { | 
|  | 164 | server["metadata"] = opts.Metadata | 
|  | 165 | } | 
| Jon Perritt | f3b2e14 | 2014-11-04 16:00:19 -0600 | [diff] [blame] | 166 | if opts.AdminPass != "" { | 
|  | 167 | server["adminPass"] = opts.AdminPass | 
|  | 168 | } | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 169 |  | 
|  | 170 | if len(opts.SecurityGroups) > 0 { | 
|  | 171 | securityGroups := make([]map[string]interface{}, len(opts.SecurityGroups)) | 
|  | 172 | for i, groupName := range opts.SecurityGroups { | 
|  | 173 | securityGroups[i] = map[string]interface{}{"name": groupName} | 
|  | 174 | } | 
| esell | df70994 | 2014-11-13 21:07:11 -0700 | [diff] [blame] | 175 | server["security_groups"] = securityGroups | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 176 | } | 
| Jon Perritt | 2a7797d | 2014-10-21 15:08:43 -0500 | [diff] [blame] | 177 |  | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 178 | if len(opts.Networks) > 0 { | 
|  | 179 | networks := make([]map[string]interface{}, len(opts.Networks)) | 
|  | 180 | for i, net := range opts.Networks { | 
|  | 181 | networks[i] = make(map[string]interface{}) | 
|  | 182 | if net.UUID != "" { | 
|  | 183 | networks[i]["uuid"] = net.UUID | 
|  | 184 | } | 
|  | 185 | if net.Port != "" { | 
|  | 186 | networks[i]["port"] = net.Port | 
|  | 187 | } | 
|  | 188 | if net.FixedIP != "" { | 
|  | 189 | networks[i]["fixed_ip"] = net.FixedIP | 
|  | 190 | } | 
|  | 191 | } | 
| Jon Perritt | 2a7797d | 2014-10-21 15:08:43 -0500 | [diff] [blame] | 192 | server["networks"] = networks | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 193 | } | 
|  | 194 |  | 
| Jon Perritt | 4149d7c | 2014-10-23 21:23:46 -0500 | [diff] [blame] | 195 | return map[string]interface{}{"server": server}, nil | 
| Ash Wilson | 6a310e0 | 2014-09-29 08:24:02 -0400 | [diff] [blame] | 196 | } | 
|  | 197 |  | 
| Samuel A. Falvo II | ce00073 | 2014-02-13 18:53:53 -0800 | [diff] [blame] | 198 | // Create requests a server to be provisioned to the user in the current tenant. | 
| Ash Wilson | 2206a11 | 2014-10-02 10:57:38 -0400 | [diff] [blame] | 199 | func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { | 
| Jon Perritt | 4149d7c | 2014-10-23 21:23:46 -0500 | [diff] [blame] | 200 | var res CreateResult | 
|  | 201 |  | 
|  | 202 | reqBody, err := opts.ToServerCreateMap() | 
|  | 203 | if err != nil { | 
|  | 204 | res.Err = err | 
|  | 205 | return res | 
|  | 206 | } | 
|  | 207 |  | 
|  | 208 | _, res.Err = perigee.Request("POST", listURL(client), perigee.Options{ | 
|  | 209 | Results:     &res.Body, | 
|  | 210 | ReqBody:     reqBody, | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 211 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 212 | OkCodes:     []int{202}, | 
| Samuel A. Falvo II | ce00073 | 2014-02-13 18:53:53 -0800 | [diff] [blame] | 213 | }) | 
| Jon Perritt | 4149d7c | 2014-10-23 21:23:46 -0500 | [diff] [blame] | 214 | return res | 
| Samuel A. Falvo II | ce00073 | 2014-02-13 18:53:53 -0800 | [diff] [blame] | 215 | } | 
|  | 216 |  | 
|  | 217 | // Delete requests that a server previously provisioned be removed from your account. | 
| Jamie Hannaford | 34732fe | 2014-10-27 11:29:36 +0100 | [diff] [blame] | 218 | func Delete(client *gophercloud.ServiceClient, id string) DeleteResult { | 
|  | 219 | var res DeleteResult | 
|  | 220 | _, res.Err = perigee.Request("DELETE", deleteURL(client, id), perigee.Options{ | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 221 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Samuel A. Falvo II | e246ac0 | 2014-02-13 23:20:09 -0800 | [diff] [blame] | 222 | OkCodes:     []int{204}, | 
| Samuel A. Falvo II | ce00073 | 2014-02-13 18:53:53 -0800 | [diff] [blame] | 223 | }) | 
| Jamie Hannaford | 34732fe | 2014-10-27 11:29:36 +0100 | [diff] [blame] | 224 | return res | 
| Samuel A. Falvo II | ce00073 | 2014-02-13 18:53:53 -0800 | [diff] [blame] | 225 | } | 
|  | 226 |  | 
| Ash Wilson | 7ddf036 | 2014-09-17 10:59:09 -0400 | [diff] [blame] | 227 | // Get requests details on a single server, by ID. | 
| Ash Wilson | d27e0ff | 2014-09-25 11:50:31 -0400 | [diff] [blame] | 228 | func Get(client *gophercloud.ServiceClient, id string) GetResult { | 
|  | 229 | var result GetResult | 
| Jon Perritt | 703bfc0 | 2014-10-08 14:35:00 -0500 | [diff] [blame] | 230 | _, result.Err = perigee.Request("GET", getURL(client, id), perigee.Options{ | 
| Ash Wilson | d3dc254 | 2014-10-20 10:10:48 -0400 | [diff] [blame] | 231 | Results:     &result.Body, | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 232 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Samuel A. Falvo II | ce00073 | 2014-02-13 18:53:53 -0800 | [diff] [blame] | 233 | }) | 
| Ash Wilson | d27e0ff | 2014-09-25 11:50:31 -0400 | [diff] [blame] | 234 | return result | 
| Samuel A. Falvo II | ce00073 | 2014-02-13 18:53:53 -0800 | [diff] [blame] | 235 | } | 
|  | 236 |  | 
| Alex Gaynor | a6d5f9f | 2014-10-27 10:52:32 -0700 | [diff] [blame] | 237 | // UpdateOptsBuilder allows extensions to add additional attributes to the Update request. | 
| Jon Perritt | 8204821 | 2014-10-13 22:33:13 -0500 | [diff] [blame] | 238 | type UpdateOptsBuilder interface { | 
| Ash Wilson | e45c973 | 2014-09-29 10:54:12 -0400 | [diff] [blame] | 239 | ToServerUpdateMap() map[string]interface{} | 
| Ash Wilson | dcbc8fb | 2014-09-29 09:05:44 -0400 | [diff] [blame] | 240 | } | 
|  | 241 |  | 
|  | 242 | // UpdateOpts specifies the base attributes that may be updated on an existing server. | 
|  | 243 | type UpdateOpts struct { | 
|  | 244 | // Name [optional] changes the displayed name of the server. | 
|  | 245 | // The server host name will *not* change. | 
|  | 246 | // Server names are not constrained to be unique, even within the same tenant. | 
|  | 247 | Name string | 
|  | 248 |  | 
|  | 249 | // AccessIPv4 [optional] provides a new IPv4 address for the instance. | 
|  | 250 | AccessIPv4 string | 
|  | 251 |  | 
|  | 252 | // AccessIPv6 [optional] provides a new IPv6 address for the instance. | 
|  | 253 | AccessIPv6 string | 
|  | 254 | } | 
|  | 255 |  | 
| Ash Wilson | e45c973 | 2014-09-29 10:54:12 -0400 | [diff] [blame] | 256 | // ToServerUpdateMap formats an UpdateOpts structure into a request body. | 
|  | 257 | func (opts UpdateOpts) ToServerUpdateMap() map[string]interface{} { | 
| Ash Wilson | dcbc8fb | 2014-09-29 09:05:44 -0400 | [diff] [blame] | 258 | server := make(map[string]string) | 
|  | 259 | if opts.Name != "" { | 
|  | 260 | server["name"] = opts.Name | 
|  | 261 | } | 
|  | 262 | if opts.AccessIPv4 != "" { | 
|  | 263 | server["accessIPv4"] = opts.AccessIPv4 | 
|  | 264 | } | 
|  | 265 | if opts.AccessIPv6 != "" { | 
|  | 266 | server["accessIPv6"] = opts.AccessIPv6 | 
|  | 267 | } | 
|  | 268 | return map[string]interface{}{"server": server} | 
|  | 269 | } | 
|  | 270 |  | 
| Samuel A. Falvo II | 22d3b77 | 2014-02-13 19:27:05 -0800 | [diff] [blame] | 271 | // Update requests that various attributes of the indicated server be changed. | 
| Jon Perritt | 8204821 | 2014-10-13 22:33:13 -0500 | [diff] [blame] | 272 | func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult { | 
| Ash Wilson | d27e0ff | 2014-09-25 11:50:31 -0400 | [diff] [blame] | 273 | var result UpdateResult | 
| Jon Perritt | 703bfc0 | 2014-10-08 14:35:00 -0500 | [diff] [blame] | 274 | _, result.Err = perigee.Request("PUT", updateURL(client, id), perigee.Options{ | 
| Ash Wilson | d3dc254 | 2014-10-20 10:10:48 -0400 | [diff] [blame] | 275 | Results:     &result.Body, | 
| Ash Wilson | e45c973 | 2014-09-29 10:54:12 -0400 | [diff] [blame] | 276 | ReqBody:     opts.ToServerUpdateMap(), | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 277 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Samuel A. Falvo II | 22d3b77 | 2014-02-13 19:27:05 -0800 | [diff] [blame] | 278 | }) | 
| Ash Wilson | d27e0ff | 2014-09-25 11:50:31 -0400 | [diff] [blame] | 279 | return result | 
| Samuel A. Falvo II | 22d3b77 | 2014-02-13 19:27:05 -0800 | [diff] [blame] | 280 | } | 
| Samuel A. Falvo II | ca5f9a3 | 2014-03-11 17:52:58 -0700 | [diff] [blame] | 281 |  | 
| Ash Wilson | 01626a3 | 2014-09-17 10:38:07 -0400 | [diff] [blame] | 282 | // ChangeAdminPassword alters the administrator or root password for a specified server. | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 283 | func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) ActionResult { | 
| Ash Wilson | dc7daa8 | 2014-09-23 16:34:42 -0400 | [diff] [blame] | 284 | var req struct { | 
|  | 285 | ChangePassword struct { | 
|  | 286 | AdminPass string `json:"adminPass"` | 
|  | 287 | } `json:"changePassword"` | 
|  | 288 | } | 
|  | 289 |  | 
|  | 290 | req.ChangePassword.AdminPass = newPassword | 
|  | 291 |  | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 292 | var res ActionResult | 
|  | 293 |  | 
|  | 294 | _, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{ | 
| Ash Wilson | dc7daa8 | 2014-09-23 16:34:42 -0400 | [diff] [blame] | 295 | ReqBody:     req, | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 296 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 297 | OkCodes:     []int{202}, | 
| Samuel A. Falvo II | ca5f9a3 | 2014-03-11 17:52:58 -0700 | [diff] [blame] | 298 | }) | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 299 |  | 
|  | 300 | return res | 
| Samuel A. Falvo II | ca5f9a3 | 2014-03-11 17:52:58 -0700 | [diff] [blame] | 301 | } | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 302 |  | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 303 | // ErrArgument errors occur when an argument supplied to a package function | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 304 | // fails to fall within acceptable values.  For example, the Reboot() function | 
|  | 305 | // expects the "how" parameter to be one of HardReboot or SoftReboot.  These | 
|  | 306 | // constants are (currently) strings, leading someone to wonder if they can pass | 
|  | 307 | // other string values instead, perhaps in an effort to break the API of their | 
|  | 308 | // provider.  Reboot() returns this error in this situation. | 
|  | 309 | // | 
|  | 310 | // Function identifies which function was called/which function is generating | 
|  | 311 | // the error. | 
|  | 312 | // Argument identifies which formal argument was responsible for producing the | 
|  | 313 | // error. | 
|  | 314 | // Value provides the value as it was passed into the function. | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 315 | type ErrArgument struct { | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 316 | Function, Argument string | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 317 | Value              interface{} | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 318 | } | 
|  | 319 |  | 
|  | 320 | // Error yields a useful diagnostic for debugging purposes. | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 321 | func (e *ErrArgument) Error() string { | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 322 | return fmt.Sprintf("Bad argument in call to %s, formal parameter %s, value %#v", e.Function, e.Argument, e.Value) | 
|  | 323 | } | 
|  | 324 |  | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 325 | func (e *ErrArgument) String() string { | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 326 | return e.Error() | 
|  | 327 | } | 
|  | 328 |  | 
| Ash Wilson | 01626a3 | 2014-09-17 10:38:07 -0400 | [diff] [blame] | 329 | // RebootMethod describes the mechanisms by which a server reboot can be requested. | 
|  | 330 | type RebootMethod string | 
|  | 331 |  | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 332 | // These constants determine how a server should be rebooted. | 
|  | 333 | // See the Reboot() function for further details. | 
|  | 334 | const ( | 
| Ash Wilson | 01626a3 | 2014-09-17 10:38:07 -0400 | [diff] [blame] | 335 | SoftReboot RebootMethod = "SOFT" | 
|  | 336 | HardReboot RebootMethod = "HARD" | 
|  | 337 | OSReboot                = SoftReboot | 
|  | 338 | PowerCycle              = HardReboot | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 339 | ) | 
|  | 340 |  | 
|  | 341 | // Reboot requests that a given server reboot. | 
|  | 342 | // Two methods exist for rebooting a server: | 
|  | 343 | // | 
| Ash Wilson | 01626a3 | 2014-09-17 10:38:07 -0400 | [diff] [blame] | 344 | // HardReboot (aka PowerCycle) restarts the server instance by physically cutting power to the machine, or if a VM, | 
|  | 345 | // terminating it at the hypervisor level. | 
|  | 346 | // It's done. Caput. Full stop. | 
|  | 347 | // Then, after a brief while, power is restored or the VM instance restarted. | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 348 | // | 
| Ash Wilson | 01626a3 | 2014-09-17 10:38:07 -0400 | [diff] [blame] | 349 | // SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures. | 
|  | 350 | // E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine. | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 351 | func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) ActionResult { | 
|  | 352 | var res ActionResult | 
|  | 353 |  | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 354 | if (how != SoftReboot) && (how != HardReboot) { | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 355 | res.Err = &ErrArgument{ | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 356 | Function: "Reboot", | 
|  | 357 | Argument: "how", | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 358 | Value:    how, | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 359 | } | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 360 | return res | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 361 | } | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 362 |  | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 363 | _, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{ | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 364 | ReqBody: struct { | 
|  | 365 | C map[string]string `json:"reboot"` | 
|  | 366 | }{ | 
| Ash Wilson | 01626a3 | 2014-09-17 10:38:07 -0400 | [diff] [blame] | 367 | map[string]string{"type": string(how)}, | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 368 | }, | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 369 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 370 | OkCodes:     []int{202}, | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 371 | }) | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 372 |  | 
|  | 373 | return res | 
| Samuel A. Falvo II | 41c9f61 | 2014-03-11 19:00:10 -0700 | [diff] [blame] | 374 | } | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 375 |  | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 376 | // RebuildOptsBuilder is an interface that allows extensions to override the | 
|  | 377 | // default behaviour of rebuild options | 
|  | 378 | type RebuildOptsBuilder interface { | 
|  | 379 | ToServerRebuildMap() (map[string]interface{}, error) | 
|  | 380 | } | 
|  | 381 |  | 
|  | 382 | // RebuildOpts represents the configuration options used in a server rebuild | 
|  | 383 | // operation | 
|  | 384 | type RebuildOpts struct { | 
|  | 385 | // Required. The ID of the image you want your server to be provisioned on | 
| Jamie Hannaford | dcb8c27 | 2014-10-16 16:34:41 +0200 | [diff] [blame] | 386 | ImageID string | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 387 |  | 
|  | 388 | // Name to set the server to | 
| Jamie Hannaford | dcb8c27 | 2014-10-16 16:34:41 +0200 | [diff] [blame] | 389 | Name string | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 390 |  | 
|  | 391 | // Required. The server's admin password | 
| Jamie Hannaford | dcb8c27 | 2014-10-16 16:34:41 +0200 | [diff] [blame] | 392 | AdminPass string | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 393 |  | 
|  | 394 | // AccessIPv4 [optional] provides a new IPv4 address for the instance. | 
| Jamie Hannaford | dcb8c27 | 2014-10-16 16:34:41 +0200 | [diff] [blame] | 395 | AccessIPv4 string | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 396 |  | 
|  | 397 | // AccessIPv6 [optional] provides a new IPv6 address for the instance. | 
| Jamie Hannaford | dcb8c27 | 2014-10-16 16:34:41 +0200 | [diff] [blame] | 398 | AccessIPv6 string | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 399 |  | 
|  | 400 | // Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server. | 
| Jamie Hannaford | dcb8c27 | 2014-10-16 16:34:41 +0200 | [diff] [blame] | 401 | Metadata map[string]string | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 402 |  | 
|  | 403 | // Personality [optional] includes the path and contents of a file to inject into the server at launch. | 
|  | 404 | // The maximum size of the file is 255 bytes (decoded). | 
| Jamie Hannaford | dcb8c27 | 2014-10-16 16:34:41 +0200 | [diff] [blame] | 405 | Personality []byte | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 406 | } | 
|  | 407 |  | 
|  | 408 | // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON | 
|  | 409 | func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { | 
|  | 410 | var err error | 
|  | 411 | server := make(map[string]interface{}) | 
|  | 412 |  | 
|  | 413 | if opts.AdminPass == "" { | 
|  | 414 | err = fmt.Errorf("AdminPass is required") | 
|  | 415 | } | 
|  | 416 |  | 
|  | 417 | if opts.ImageID == "" { | 
|  | 418 | err = fmt.Errorf("ImageID is required") | 
|  | 419 | } | 
|  | 420 |  | 
|  | 421 | if err != nil { | 
|  | 422 | return server, err | 
|  | 423 | } | 
|  | 424 |  | 
|  | 425 | server["name"] = opts.Name | 
|  | 426 | server["adminPass"] = opts.AdminPass | 
|  | 427 | server["imageRef"] = opts.ImageID | 
|  | 428 |  | 
|  | 429 | if opts.AccessIPv4 != "" { | 
|  | 430 | server["accessIPv4"] = opts.AccessIPv4 | 
|  | 431 | } | 
|  | 432 |  | 
|  | 433 | if opts.AccessIPv6 != "" { | 
|  | 434 | server["accessIPv6"] = opts.AccessIPv6 | 
|  | 435 | } | 
|  | 436 |  | 
|  | 437 | if opts.Metadata != nil { | 
|  | 438 | server["metadata"] = opts.Metadata | 
|  | 439 | } | 
|  | 440 |  | 
|  | 441 | if opts.Personality != nil { | 
|  | 442 | encoded := base64.StdEncoding.EncodeToString(opts.Personality) | 
|  | 443 | server["personality"] = &encoded | 
|  | 444 | } | 
|  | 445 |  | 
|  | 446 | return map[string]interface{}{"rebuild": server}, nil | 
|  | 447 | } | 
|  | 448 |  | 
|  | 449 | // Rebuild will reprovision the server according to the configuration options | 
|  | 450 | // provided in the RebuildOpts struct. | 
|  | 451 | func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) RebuildResult { | 
| Ash Wilson | d27e0ff | 2014-09-25 11:50:31 -0400 | [diff] [blame] | 452 | var result RebuildResult | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 453 |  | 
|  | 454 | if id == "" { | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 455 | result.Err = fmt.Errorf("ID is required") | 
| Ash Wilson | d27e0ff | 2014-09-25 11:50:31 -0400 | [diff] [blame] | 456 | return result | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 457 | } | 
|  | 458 |  | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 459 | reqBody, err := opts.ToServerRebuildMap() | 
|  | 460 | if err != nil { | 
|  | 461 | result.Err = err | 
| Ash Wilson | d27e0ff | 2014-09-25 11:50:31 -0400 | [diff] [blame] | 462 | return result | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 463 | } | 
|  | 464 |  | 
| Ash Wilson | 31f6bde | 2014-09-25 14:52:12 -0400 | [diff] [blame] | 465 | _, result.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{ | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 466 | ReqBody:     &reqBody, | 
| Ash Wilson | d3dc254 | 2014-10-20 10:10:48 -0400 | [diff] [blame] | 467 | Results:     &result.Body, | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 468 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 469 | OkCodes:     []int{202}, | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 470 | }) | 
| Jamie Hannaford | 6c9eb60 | 2014-10-16 16:28:07 +0200 | [diff] [blame] | 471 |  | 
| Ash Wilson | d27e0ff | 2014-09-25 11:50:31 -0400 | [diff] [blame] | 472 | return result | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 473 | } | 
|  | 474 |  | 
| Ash Wilson | 5f7cf18 | 2014-10-23 08:35:24 -0400 | [diff] [blame] | 475 | // ResizeOptsBuilder is an interface that allows extensions to override the default structure of | 
|  | 476 | // a Resize request. | 
|  | 477 | type ResizeOptsBuilder interface { | 
|  | 478 | ToServerResizeMap() (map[string]interface{}, error) | 
|  | 479 | } | 
|  | 480 |  | 
|  | 481 | // ResizeOpts represents the configuration options used to control a Resize operation. | 
|  | 482 | type ResizeOpts struct { | 
|  | 483 | // FlavorRef is the ID of the flavor you wish your server to become. | 
|  | 484 | FlavorRef string | 
|  | 485 | } | 
|  | 486 |  | 
| Alex Gaynor | 266e933 | 2014-10-28 14:44:04 -0700 | [diff] [blame] | 487 | // ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON request body for the | 
| Ash Wilson | 5f7cf18 | 2014-10-23 08:35:24 -0400 | [diff] [blame] | 488 | // Resize request. | 
|  | 489 | func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { | 
|  | 490 | resize := map[string]interface{}{ | 
|  | 491 | "flavorRef": opts.FlavorRef, | 
|  | 492 | } | 
|  | 493 |  | 
|  | 494 | return map[string]interface{}{"resize": resize}, nil | 
|  | 495 | } | 
|  | 496 |  | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 497 | // Resize instructs the provider to change the flavor of the server. | 
| Ash Wilson | 01626a3 | 2014-09-17 10:38:07 -0400 | [diff] [blame] | 498 | // Note that this implies rebuilding it. | 
|  | 499 | // Unfortunately, one cannot pass rebuild parameters to the resize function. | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 500 | // When the resize completes, the server will be in RESIZE_VERIFY state. | 
|  | 501 | // While in this state, you can explore the use of the new server's configuration. | 
|  | 502 | // If you like it, call ConfirmResize() to commit the resize permanently. | 
|  | 503 | // Otherwise, call RevertResize() to restore the old configuration. | 
| Ash Wilson | 9e87a92 | 2014-10-23 14:29:22 -0400 | [diff] [blame] | 504 | func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) ActionResult { | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 505 | var res ActionResult | 
| Ash Wilson | 5f7cf18 | 2014-10-23 08:35:24 -0400 | [diff] [blame] | 506 | reqBody, err := opts.ToServerResizeMap() | 
|  | 507 | if err != nil { | 
|  | 508 | res.Err = err | 
|  | 509 | return res | 
|  | 510 | } | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 511 |  | 
|  | 512 | _, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{ | 
| Ash Wilson | 5f7cf18 | 2014-10-23 08:35:24 -0400 | [diff] [blame] | 513 | ReqBody:     reqBody, | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 514 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 515 | OkCodes:     []int{202}, | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 516 | }) | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 517 |  | 
|  | 518 | return res | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 519 | } | 
|  | 520 |  | 
|  | 521 | // ConfirmResize confirms a previous resize operation on a server. | 
|  | 522 | // See Resize() for more details. | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 523 | func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult { | 
|  | 524 | var res ActionResult | 
|  | 525 |  | 
|  | 526 | _, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{ | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 527 | ReqBody:     map[string]interface{}{"confirmResize": nil}, | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 528 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 529 | OkCodes:     []int{204}, | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 530 | }) | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 531 |  | 
|  | 532 | return res | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 533 | } | 
|  | 534 |  | 
|  | 535 | // RevertResize cancels a previous resize operation on a server. | 
|  | 536 | // See Resize() for more details. | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 537 | func RevertResize(client *gophercloud.ServiceClient, id string) ActionResult { | 
|  | 538 | var res ActionResult | 
|  | 539 |  | 
|  | 540 | _, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{ | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 541 | ReqBody:     map[string]interface{}{"revertResize": nil}, | 
| Ash Wilson | 77857dc | 2014-10-22 09:09:02 -0400 | [diff] [blame] | 542 | MoreHeaders: client.AuthenticatedHeaders(), | 
| Jon Perritt | 3055864 | 2014-04-14 17:07:12 -0500 | [diff] [blame] | 543 | OkCodes:     []int{202}, | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 544 | }) | 
| Jamie Hannaford | 8c072a3 | 2014-10-16 14:33:32 +0200 | [diff] [blame] | 545 |  | 
|  | 546 | return res | 
| Samuel A. Falvo II | 808bb63 | 2014-03-12 00:07:50 -0700 | [diff] [blame] | 547 | } | 
| Alex Gaynor | 39584a0 | 2014-10-28 13:59:21 -0700 | [diff] [blame] | 548 |  | 
| Alex Gaynor | 266e933 | 2014-10-28 14:44:04 -0700 | [diff] [blame] | 549 | // RescueOptsBuilder is an interface that allows extensions to override the | 
|  | 550 | // default structure of a Rescue request. | 
| Alex Gaynor | 39584a0 | 2014-10-28 13:59:21 -0700 | [diff] [blame] | 551 | type RescueOptsBuilder interface { | 
|  | 552 | ToServerRescueMap() (map[string]interface{}, error) | 
|  | 553 | } | 
|  | 554 |  | 
| Alex Gaynor | 266e933 | 2014-10-28 14:44:04 -0700 | [diff] [blame] | 555 | // RescueOpts represents the configuration options used to control a Rescue | 
|  | 556 | // option. | 
| Alex Gaynor | 39584a0 | 2014-10-28 13:59:21 -0700 | [diff] [blame] | 557 | type RescueOpts struct { | 
| Alex Gaynor | 266e933 | 2014-10-28 14:44:04 -0700 | [diff] [blame] | 558 | // AdminPass is the desired administrative password for the instance in | 
| Alex Gaynor | cfec772 | 2014-11-13 13:33:49 -0800 | [diff] [blame] | 559 | // RESCUE mode. If it's left blank, the server will generate a password. | 
| Alex Gaynor | 39584a0 | 2014-10-28 13:59:21 -0700 | [diff] [blame] | 560 | AdminPass string | 
|  | 561 | } | 
|  | 562 |  | 
| Jon Perritt | cc77da6 | 2014-11-16 13:14:21 -0700 | [diff] [blame] | 563 | // ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON | 
| Alex Gaynor | 266e933 | 2014-10-28 14:44:04 -0700 | [diff] [blame] | 564 | // request body for the Rescue request. | 
| Alex Gaynor | 39584a0 | 2014-10-28 13:59:21 -0700 | [diff] [blame] | 565 | func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { | 
|  | 566 | server := make(map[string]interface{}) | 
|  | 567 | if opts.AdminPass != "" { | 
|  | 568 | server["adminPass"] = opts.AdminPass | 
|  | 569 | } | 
|  | 570 | return map[string]interface{}{"rescue": server}, nil | 
|  | 571 | } | 
|  | 572 |  | 
| Alex Gaynor | 266e933 | 2014-10-28 14:44:04 -0700 | [diff] [blame] | 573 | // Rescue instructs the provider to place the server into RESCUE mode. | 
| Alex Gaynor | fbe61bb | 2014-11-12 13:35:03 -0800 | [diff] [blame] | 574 | func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) RescueResult { | 
|  | 575 | var result RescueResult | 
| Alex Gaynor | 39584a0 | 2014-10-28 13:59:21 -0700 | [diff] [blame] | 576 |  | 
|  | 577 | if id == "" { | 
|  | 578 | result.Err = fmt.Errorf("ID is required") | 
|  | 579 | return result | 
|  | 580 | } | 
|  | 581 | reqBody, err := opts.ToServerRescueMap() | 
|  | 582 | if err != nil { | 
|  | 583 | result.Err = err | 
|  | 584 | return result | 
|  | 585 | } | 
|  | 586 |  | 
|  | 587 | _, result.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{ | 
| Alex Gaynor | fbe61bb | 2014-11-12 13:35:03 -0800 | [diff] [blame] | 588 | Results:     &result.Body, | 
| Alex Gaynor | 39584a0 | 2014-10-28 13:59:21 -0700 | [diff] [blame] | 589 | ReqBody:     &reqBody, | 
| Alex Gaynor | 39584a0 | 2014-10-28 13:59:21 -0700 | [diff] [blame] | 590 | MoreHeaders: client.AuthenticatedHeaders(), | 
|  | 591 | OkCodes:     []int{200}, | 
|  | 592 | }) | 
|  | 593 |  | 
|  | 594 | return result | 
|  | 595 | } | 
| Jon Perritt | cc77da6 | 2014-11-16 13:14:21 -0700 | [diff] [blame] | 596 |  | 
|  | 597 | // CreateMetadatasOptsBuilder allows extensions to add additional parameters to the | 
|  | 598 | // Create request. | 
|  | 599 | type CreateMetadatasOptsBuilder interface { | 
|  | 600 | ToMetadatasCreateMap() (map[string]interface{}, error) | 
|  | 601 | } | 
|  | 602 |  | 
|  | 603 | // MetadatasOpts is a map that contains key-value pairs. | 
|  | 604 | type MetadatasOpts map[string]string | 
|  | 605 |  | 
|  | 606 | // ToMetadatasCreateMap assembles a body for a Create request based on the contents of a MetadatasOpts. | 
|  | 607 | func (opts MetadatasOpts) ToMetadatasCreateMap() (map[string]interface{}, error) { | 
|  | 608 | return map[string]interface{}{"metadata": opts}, nil | 
|  | 609 | } | 
|  | 610 |  | 
|  | 611 | // UpdateMetadatasOptsBuilder allows extensions to add additional parameters to the | 
|  | 612 | // Create request. | 
|  | 613 | type UpdateMetadatasOptsBuilder interface { | 
|  | 614 | ToMetadatasUpdateMap() (map[string]interface{}, error) | 
|  | 615 | } | 
|  | 616 |  | 
|  | 617 | // ToMetadatasUpdateMap assembles a body for an Update request based on the contents of a MetadatasOpts. | 
|  | 618 | func (opts MetadatasOpts) ToMetadatasUpdateMap() (map[string]interface{}, error) { | 
|  | 619 | return map[string]interface{}{"metadata": opts}, nil | 
|  | 620 | } | 
|  | 621 |  | 
|  | 622 | // CreateMetadatas will create multiple new key-value pairs for the given server ID. | 
|  | 623 | // Note: Using this operation will erase any already-existing metadata and create | 
|  | 624 | // the new metadata provided. To keep any already-existing metadata, use the | 
|  | 625 | // UpdateMetadatas or UpdateMetadata function. | 
|  | 626 | func CreateMetadatas(client *gophercloud.ServiceClient, id string, opts CreateMetadatasOptsBuilder) CreateMetadatasResult { | 
|  | 627 | var res CreateMetadatasResult | 
|  | 628 | metadatas, err := opts.ToMetadatasCreateMap() | 
|  | 629 | if err != nil { | 
|  | 630 | res.Err = err | 
|  | 631 | return res | 
|  | 632 | } | 
|  | 633 | _, res.Err = perigee.Request("PUT", metadatasURL(client, id), perigee.Options{ | 
|  | 634 | ReqBody:     metadatas, | 
|  | 635 | Results:     &res.Body, | 
|  | 636 | MoreHeaders: client.AuthenticatedHeaders(), | 
|  | 637 | }) | 
|  | 638 | return res | 
|  | 639 | } | 
|  | 640 |  | 
|  | 641 | // Metadatas requests all the metadata for the given server ID. | 
|  | 642 | func Metadatas(client *gophercloud.ServiceClient, id string) GetMetadatasResult { | 
|  | 643 | var res GetMetadatasResult | 
|  | 644 | _, res.Err = perigee.Request("GET", metadatasURL(client, id), perigee.Options{ | 
|  | 645 | Results:     &res.Body, | 
|  | 646 | MoreHeaders: client.AuthenticatedHeaders(), | 
|  | 647 | }) | 
|  | 648 | return res | 
|  | 649 | } | 
|  | 650 |  | 
|  | 651 | // UpdateMetadatas updates (or creates) all the metadata specified by opts for the given server ID. | 
|  | 652 | // This operation does not affect already-existing metadata that is not specified | 
|  | 653 | // by opts. | 
|  | 654 | func UpdateMetadatas(client *gophercloud.ServiceClient, id string, opts UpdateMetadatasOptsBuilder) UpdateMetadatasResult { | 
|  | 655 | var res UpdateMetadatasResult | 
|  | 656 | metadatas, err := opts.ToMetadatasUpdateMap() | 
|  | 657 | if err != nil { | 
|  | 658 | res.Err = err | 
|  | 659 | return res | 
|  | 660 | } | 
|  | 661 | _, res.Err = perigee.Request("POST", metadatasURL(client, id), perigee.Options{ | 
|  | 662 | ReqBody:     metadatas, | 
|  | 663 | Results:     &res.Body, | 
|  | 664 | MoreHeaders: client.AuthenticatedHeaders(), | 
|  | 665 | }) | 
|  | 666 | return res | 
|  | 667 | } | 
|  | 668 |  | 
|  | 669 | // MetadataOptsBuilder allows extensions to add additional parameters to the | 
|  | 670 | // Create request. | 
|  | 671 | type MetadataOptsBuilder interface { | 
|  | 672 | ToMetadataCreateMap() (map[string]interface{}, string, error) | 
|  | 673 | } | 
|  | 674 |  | 
|  | 675 | // MetadataOpts is a map of length one that contains a key-value pair. | 
|  | 676 | type MetadataOpts map[string]string | 
|  | 677 |  | 
|  | 678 | // ToMetadataCreateMap assembles a body for a Create request based on the contents of a MetadatasOpts. | 
|  | 679 | func (opts MetadataOpts) ToMetadataCreateMap() (map[string]interface{}, string, error) { | 
|  | 680 | if len(opts) != 1 { | 
|  | 681 | return nil, "", errors.New("CreateMetadata operation must have 1 and only 1 key-value pair.") | 
|  | 682 | } | 
|  | 683 | metadata := map[string]interface{}{"meta": opts} | 
|  | 684 | var key string | 
|  | 685 | for k := range metadata["meta"].(MetadataOpts) { | 
|  | 686 | key = k | 
|  | 687 | } | 
|  | 688 | return metadata, key, nil | 
|  | 689 | } | 
|  | 690 |  | 
|  | 691 | // CreateMetadata will create or update the key-value pair with the given key for the given server ID. | 
|  | 692 | func CreateMetadata(client *gophercloud.ServiceClient, id string, opts MetadataOptsBuilder) CreateMetadataResult { | 
|  | 693 | var res CreateMetadataResult | 
|  | 694 | metadata, key, err := opts.ToMetadataCreateMap() | 
|  | 695 | if err != nil { | 
|  | 696 | res.Err = err | 
|  | 697 | return res | 
|  | 698 | } | 
|  | 699 |  | 
|  | 700 | _, res.Err = perigee.Request("PUT", metadataURL(client, id, key), perigee.Options{ | 
|  | 701 | ReqBody:     metadata, | 
|  | 702 | Results:     &res.Body, | 
|  | 703 | MoreHeaders: client.AuthenticatedHeaders(), | 
|  | 704 | }) | 
|  | 705 | return res | 
|  | 706 | } | 
|  | 707 |  | 
|  | 708 | // Metadata requests the key-value pair with the given key for the given server ID. | 
|  | 709 | func Metadata(client *gophercloud.ServiceClient, id, key string) GetMetadataResult { | 
|  | 710 | var res GetMetadataResult | 
|  | 711 | _, res.Err = perigee.Request("GET", metadataURL(client, id, key), perigee.Options{ | 
|  | 712 | Results:     &res.Body, | 
|  | 713 | MoreHeaders: client.AuthenticatedHeaders(), | 
|  | 714 | }) | 
|  | 715 | return res | 
|  | 716 | } | 
|  | 717 |  | 
|  | 718 | // DeleteMetadata will delete the key-value pair with the given key for the given server ID. | 
|  | 719 | func DeleteMetadata(client *gophercloud.ServiceClient, id, key string) DeleteMetadataResult { | 
|  | 720 | var res DeleteMetadataResult | 
|  | 721 | _, res.Err = perigee.Request("DELETE", metadataURL(client, id, key), perigee.Options{ | 
|  | 722 | Results:     &res.Body, | 
|  | 723 | MoreHeaders: client.AuthenticatedHeaders(), | 
|  | 724 | }) | 
|  | 725 | return res | 
|  | 726 | } |