blob: f27870984ae2065d35ad250411691db9b460f385 [file] [log] [blame]
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -08001package servers
2
3import (
Jon Perritt9a0980e2015-01-14 21:29:44 -07004 "reflect"
einarf4e5fdaf2015-04-16 23:14:59 +00005 "fmt"
6 "path"
7 "net/url"
Jon Perritt9a0980e2015-01-14 21:29:44 -07008
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -08009 "github.com/mitchellh/mapstructure"
Ash Wilsond27e0ff2014-09-25 11:50:31 -040010 "github.com/rackspace/gophercloud"
Ash Wilson01626a32014-09-17 10:38:07 -040011 "github.com/rackspace/gophercloud/pagination"
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080012)
13
Ash Wilson397c78b2014-09-25 15:19:14 -040014type serverResult struct {
Ash Wilsonf548aad2014-10-20 08:35:34 -040015 gophercloud.Result
Ash Wilson397c78b2014-09-25 15:19:14 -040016}
17
18// Extract interprets any serverResult as a Server, if possible.
19func (r serverResult) Extract() (*Server, error) {
20 if r.Err != nil {
21 return nil, r.Err
22 }
23
24 var response struct {
25 Server Server `mapstructure:"server"`
26 }
27
Jon Perritt9a0980e2015-01-14 21:29:44 -070028 config := &mapstructure.DecoderConfig{
Jon Perritt0e19f602015-01-15 09:35:46 -070029 DecodeHook: toMapFromString,
30 Result: &response,
Jon Perritt9a0980e2015-01-14 21:29:44 -070031 }
32 decoder, err := mapstructure.NewDecoder(config)
33 if err != nil {
34 return nil, err
35 }
36
37 err = decoder.Decode(r.Body)
38 if err != nil {
39 return nil, err
40 }
41
42 return &response.Server, nil
Ash Wilson397c78b2014-09-25 15:19:14 -040043}
44
45// CreateResult temporarily contains the response from a Create call.
46type CreateResult struct {
47 serverResult
48}
49
50// GetResult temporarily contains the response from a Get call.
51type GetResult struct {
52 serverResult
53}
54
55// UpdateResult temporarily contains the response from an Update call.
56type UpdateResult struct {
57 serverResult
58}
59
Jon Perrittcc77da62014-11-16 13:14:21 -070060// DeleteResult temporarily contains the response from a Delete call.
Jamie Hannaford34732fe2014-10-27 11:29:36 +010061type DeleteResult struct {
Jon Perrittba2395e2014-10-27 15:23:21 -050062 gophercloud.ErrResult
Jamie Hannaford34732fe2014-10-27 11:29:36 +010063}
64
Ash Wilson397c78b2014-09-25 15:19:14 -040065// RebuildResult temporarily contains the response from a Rebuild call.
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020066type RebuildResult struct {
Ash Wilson397c78b2014-09-25 15:19:14 -040067 serverResult
68}
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080069
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020070// ActionResult represents the result of server action operations, like reboot
71type ActionResult struct {
Jon Perrittba2395e2014-10-27 15:23:21 -050072 gophercloud.ErrResult
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020073}
74
Alex Gaynor587e3e32014-11-13 10:39:09 -080075// RescueResult represents the result of a server rescue operation
Alex Gaynorfbe61bb2014-11-12 13:35:03 -080076type RescueResult struct {
77 ActionResult
Alex Gaynor7f3b06e2014-11-13 09:54:03 -080078}
79
einarf4e5fdaf2015-04-16 23:14:59 +000080// CreateImageResult represents the result of an image creation operation
81type CreateImageResult struct {
einarf2fc665e2015-04-16 20:16:21 +000082 gophercloud.Result
83}
84
einarf4e5fdaf2015-04-16 23:14:59 +000085// ExtractImageID gets the ID of the newly created server image from the header
86func (res CreateImageResult) ExtractImageID() (string, error) {
87 if res.Err != nil {
88 return "", res.Err
89 }
90 // Get the image id from the header
91 u, err := url.ParseRequestURI(res.Header.Get("Location"))
92 if err != nil {
93 return "", fmt.Errorf("Failed to parse the image id: %s", err.Error())
94 }
95 imageId := path.Base(u.Path)
96 if imageId == "." || imageId == "/" {
97 return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u)
98 }
99 return imageId, nil
100}
101
Jon Perrittcc77da62014-11-16 13:14:21 -0700102// Extract interprets any RescueResult as an AdminPass, if possible.
Alex Gaynor7f3b06e2014-11-13 09:54:03 -0800103func (r RescueResult) Extract() (string, error) {
104 if r.Err != nil {
Alex Gaynor94a28aa2014-11-13 10:09:56 -0800105 return "", r.Err
Alex Gaynor7f3b06e2014-11-13 09:54:03 -0800106 }
107
108 var response struct {
109 AdminPass string `mapstructure:"adminPass"`
110 }
111
112 err := mapstructure.Decode(r.Body, &response)
Alex Gaynor94a28aa2014-11-13 10:09:56 -0800113 return response.AdminPass, err
Alex Gaynorfbe61bb2014-11-12 13:35:03 -0800114}
115
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800116// Server exposes only the standard OpenStack fields corresponding to a given server on the user's account.
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800117type Server struct {
Ash Wilson01626a32014-09-17 10:38:07 -0400118 // ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
119 ID string
120
121 // TenantID identifies the tenant owning this server resource.
122 TenantID string `mapstructure:"tenant_id"`
123
124 // UserID uniquely identifies the user account owning the tenant.
125 UserID string `mapstructure:"user_id"`
126
127 // Name contains the human-readable name for the server.
128 Name string
129
130 // Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
131 Updated string
132 Created string
133
134 HostID string
135
136 // Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
137 Status string
138
139 // Progress ranges from 0..100.
140 // A request made against the server completes only once Progress reaches 100.
141 Progress int
142
143 // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
Jamie Hannaford908e1e92014-10-27 14:41:17 +0100144 AccessIPv4, AccessIPv6 string
Ash Wilson01626a32014-09-17 10:38:07 -0400145
146 // Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
147 Image map[string]interface{}
148
149 // Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
150 Flavor map[string]interface{}
151
152 // Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
Ash Wilson01626a32014-09-17 10:38:07 -0400153 Addresses map[string]interface{}
154
155 // Metadata includes a list of all user-specified key-value pairs attached to the server.
156 Metadata map[string]interface{}
157
158 // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
159 Links []interface{}
160
Ash Wilsonad21c712014-09-25 10:15:22 -0400161 // KeyName indicates which public key was injected into the server on launch.
Ash Wilson69b144f2014-10-21 14:03:52 -0400162 KeyName string `json:"key_name" mapstructure:"key_name"`
Ash Wilsonad21c712014-09-25 10:15:22 -0400163
Ash Wilson01626a32014-09-17 10:38:07 -0400164 // AdminPass will generally be empty (""). However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place.
165 // Note that this is the ONLY time this field will be valid.
Ash Wilson69b144f2014-10-21 14:03:52 -0400166 AdminPass string `json:"adminPass" mapstructure:"adminPass"`
Joe Topjian978bb502015-02-12 20:55:31 +0000167
168 // SecurityGroups includes the security groups that this instance has applied to it
169 SecurityGroups []map[string]interface{} `json:"security_groups" mapstructure:"security_groups"`
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800170}
171
Ash Wilson397c78b2014-09-25 15:19:14 -0400172// ServerPage abstracts the raw results of making a List() request against the API.
173// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
174// data provided through the ExtractServers call.
175type ServerPage struct {
176 pagination.LinkedPageBase
Ash Wilsonf57381e2014-09-25 13:21:34 -0400177}
178
Ash Wilson397c78b2014-09-25 15:19:14 -0400179// IsEmpty returns true if a page contains no Server results.
180func (page ServerPage) IsEmpty() (bool, error) {
181 servers, err := ExtractServers(page)
182 if err != nil {
183 return true, err
184 }
185 return len(servers) == 0, nil
186}
187
188// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
189func (page ServerPage) NextPageURL() (string, error) {
Ash Wilson397c78b2014-09-25 15:19:14 -0400190 type resp struct {
Jamie Hannaforda581acd2014-10-08 17:14:13 +0200191 Links []gophercloud.Link `mapstructure:"servers_links"`
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400192 }
193
Ash Wilson397c78b2014-09-25 15:19:14 -0400194 var r resp
195 err := mapstructure.Decode(page.Body, &r)
196 if err != nil {
197 return "", err
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400198 }
199
Jamie Hannaforda581acd2014-10-08 17:14:13 +0200200 return gophercloud.ExtractNextURL(r.Links)
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400201}
202
Ash Wilson01626a32014-09-17 10:38:07 -0400203// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
204func ExtractServers(page pagination.Page) ([]Server, error) {
Ash Wilson397c78b2014-09-25 15:19:14 -0400205 casted := page.(ServerPage).Body
Ash Wilson12259392014-09-17 10:50:02 -0400206
207 var response struct {
208 Servers []Server `mapstructure:"servers"`
209 }
Jon Perritt9a0980e2015-01-14 21:29:44 -0700210
211 config := &mapstructure.DecoderConfig{
Jon Perritt0e19f602015-01-15 09:35:46 -0700212 DecodeHook: toMapFromString,
213 Result: &response,
Jon Perritt9a0980e2015-01-14 21:29:44 -0700214 }
215 decoder, err := mapstructure.NewDecoder(config)
216 if err != nil {
217 return nil, err
218 }
219
220 err = decoder.Decode(casted)
221
Ash Wilson12259392014-09-17 10:50:02 -0400222 return response.Servers, err
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800223}
Jon Perrittcc77da62014-11-16 13:14:21 -0700224
Jon Perritt78c57ce2014-11-20 11:07:18 -0700225// MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
Jon Perrittcc77da62014-11-16 13:14:21 -0700226type MetadataResult struct {
227 gophercloud.Result
228}
229
230// GetMetadataResult temporarily contains the response from a metadata Get call.
231type GetMetadataResult struct {
232 MetadataResult
233}
234
Jon Perritt789f8322014-11-21 08:20:04 -0700235// ResetMetadataResult temporarily contains the response from a metadata Reset call.
236type ResetMetadataResult struct {
Jon Perrittcc77da62014-11-16 13:14:21 -0700237 MetadataResult
238}
239
Jon Perritt78c57ce2014-11-20 11:07:18 -0700240// UpdateMetadataResult temporarily contains the response from a metadata Update call.
241type UpdateMetadataResult struct {
242 MetadataResult
Jon Perrittcc77da62014-11-16 13:14:21 -0700243}
244
Jon Perritt78c57ce2014-11-20 11:07:18 -0700245// MetadatumResult contains the result of a call for individual a single key-value pair.
246type MetadatumResult struct {
247 gophercloud.Result
248}
Jon Perrittcc77da62014-11-16 13:14:21 -0700249
Jon Perritt78c57ce2014-11-20 11:07:18 -0700250// GetMetadatumResult temporarily contains the response from a metadatum Get call.
251type GetMetadatumResult struct {
252 MetadatumResult
253}
Jon Perrittcc77da62014-11-16 13:14:21 -0700254
Jon Perritt78c57ce2014-11-20 11:07:18 -0700255// CreateMetadatumResult temporarily contains the response from a metadatum Create call.
256type CreateMetadatumResult struct {
257 MetadatumResult
258}
259
260// DeleteMetadatumResult temporarily contains the response from a metadatum Delete call.
261type DeleteMetadatumResult struct {
262 gophercloud.ErrResult
Jon Perrittcc77da62014-11-16 13:14:21 -0700263}
264
265// Extract interprets any MetadataResult as a Metadata, if possible.
266func (r MetadataResult) Extract() (map[string]string, error) {
267 if r.Err != nil {
268 return nil, r.Err
269 }
270
271 var response struct {
Jon Perritt78c57ce2014-11-20 11:07:18 -0700272 Metadata map[string]string `mapstructure:"metadata"`
Jon Perrittcc77da62014-11-16 13:14:21 -0700273 }
274
275 err := mapstructure.Decode(r.Body, &response)
276 return response.Metadata, err
277}
Jon Perritt78c57ce2014-11-20 11:07:18 -0700278
279// Extract interprets any MetadatumResult as a Metadatum, if possible.
280func (r MetadatumResult) Extract() (map[string]string, error) {
281 if r.Err != nil {
282 return nil, r.Err
283 }
284
285 var response struct {
286 Metadatum map[string]string `mapstructure:"meta"`
287 }
288
289 err := mapstructure.Decode(r.Body, &response)
290 return response.Metadatum, err
291}
Jon Perritt0e19f602015-01-15 09:35:46 -0700292
293func toMapFromString(from reflect.Kind, to reflect.Kind, data interface{}) (interface{}, error) {
294 if (from == reflect.String) && (to == reflect.Map) {
295 return map[string]interface{}{}, nil
296 }
297 return data, nil
298}
Jon Perritt5cb49482015-02-19 12:19:58 -0700299
300// Address represents an IP address.
301type Address struct {
302 Version int `mapstructure:"version"`
303 Address string `mapstructure:"addr"`
304}
305
Jon Perritt04d073c2015-02-19 21:46:23 -0700306// AddressPage abstracts the raw results of making a ListAddresses() request against the API.
Jon Perritt5cb49482015-02-19 12:19:58 -0700307// As OpenStack extensions may freely alter the response bodies of structures returned
308// to the client, you may only safely access the data provided through the ExtractAddresses call.
309type AddressPage struct {
310 pagination.SinglePageBase
311}
312
Jon Perritt04d073c2015-02-19 21:46:23 -0700313// IsEmpty returns true if an AddressPage contains no networks.
Jon Perritt5cb49482015-02-19 12:19:58 -0700314func (r AddressPage) IsEmpty() (bool, error) {
315 addresses, err := ExtractAddresses(r)
316 if err != nil {
317 return true, err
318 }
319 return len(addresses) == 0, nil
320}
321
Jon Perritt04d073c2015-02-19 21:46:23 -0700322// ExtractAddresses interprets the results of a single page from a ListAddresses() call,
Jon Perritt5cb49482015-02-19 12:19:58 -0700323// producing a map of addresses.
324func ExtractAddresses(page pagination.Page) (map[string][]Address, error) {
325 casted := page.(AddressPage).Body
326
327 var response struct {
328 Addresses map[string][]Address `mapstructure:"addresses"`
329 }
330
331 err := mapstructure.Decode(casted, &response)
332 if err != nil {
333 return nil, err
334 }
335
336 return response.Addresses, err
337}
Jon Perritt04d073c2015-02-19 21:46:23 -0700338
339// NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() request against the API.
340// As OpenStack extensions may freely alter the response bodies of structures returned
341// to the client, you may only safely access the data provided through the ExtractAddresses call.
342type NetworkAddressPage struct {
343 pagination.SinglePageBase
344}
345
346// IsEmpty returns true if a NetworkAddressPage contains no addresses.
347func (r NetworkAddressPage) IsEmpty() (bool, error) {
348 addresses, err := ExtractNetworkAddresses(r)
349 if err != nil {
350 return true, err
351 }
352 return len(addresses) == 0, nil
353}
354
355// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call,
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700356// producing a slice of addresses.
357func ExtractNetworkAddresses(page pagination.Page) ([]Address, error) {
Jon Perritt04d073c2015-02-19 21:46:23 -0700358 casted := page.(NetworkAddressPage).Body
359
360 var response map[string][]Address
361 err := mapstructure.Decode(casted, &response)
362 if err != nil {
363 return nil, err
364 }
365
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700366 var key string
367 for k := range response {
368 key = k
369 }
370
371 return response[key], err
Jon Perritt04d073c2015-02-19 21:46:23 -0700372}