blob: 42374811561e5c6bc426af4dcec42d315f6dd636 [file] [log] [blame]
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -08001package servers
2
3import (
Rickard von Essen5b8bbff2016-02-16 07:48:20 +01004 "crypto/rsa"
5 "encoding/base64"
einarf4e5fdaf2015-04-16 23:14:59 +00006 "fmt"
einarf4e5fdaf2015-04-16 23:14:59 +00007 "net/url"
Jon Perritt12395212016-02-24 10:41:17 -06008 "path"
Jon Perritt9a0980e2015-01-14 21:29:44 -07009
Jon Perritt27249f42016-02-18 10:35:59 -060010 "github.com/gophercloud/gophercloud"
11 "github.com/gophercloud/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) {
Jon Perritt12395212016-02-24 10:41:17 -060020 var s struct {
21 Server *Server `json:"server"`
Ash Wilson397c78b2014-09-25 15:19:14 -040022 }
Jon Perritt12395212016-02-24 10:41:17 -060023 err := r.ExtractInto(&s)
24 return s.Server, err
Ash Wilson397c78b2014-09-25 15:19:14 -040025}
26
27// CreateResult temporarily contains the response from a Create call.
28type CreateResult struct {
29 serverResult
30}
31
32// GetResult temporarily contains the response from a Get call.
33type GetResult struct {
34 serverResult
35}
36
37// UpdateResult temporarily contains the response from an Update call.
38type UpdateResult struct {
39 serverResult
40}
41
Jon Perrittcc77da62014-11-16 13:14:21 -070042// DeleteResult temporarily contains the response from a Delete call.
Jamie Hannaford34732fe2014-10-27 11:29:36 +010043type DeleteResult struct {
Jon Perrittba2395e2014-10-27 15:23:21 -050044 gophercloud.ErrResult
Jamie Hannaford34732fe2014-10-27 11:29:36 +010045}
46
Ash Wilson397c78b2014-09-25 15:19:14 -040047// RebuildResult temporarily contains the response from a Rebuild call.
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020048type RebuildResult struct {
Ash Wilson397c78b2014-09-25 15:19:14 -040049 serverResult
50}
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080051
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020052// ActionResult represents the result of server action operations, like reboot
53type ActionResult struct {
Jon Perrittba2395e2014-10-27 15:23:21 -050054 gophercloud.ErrResult
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020055}
56
Alex Gaynor587e3e32014-11-13 10:39:09 -080057// RescueResult represents the result of a server rescue operation
Alex Gaynorfbe61bb2014-11-12 13:35:03 -080058type RescueResult struct {
59 ActionResult
Alex Gaynor7f3b06e2014-11-13 09:54:03 -080060}
61
einarf4e5fdaf2015-04-16 23:14:59 +000062// CreateImageResult represents the result of an image creation operation
63type CreateImageResult struct {
einarf2fc665e2015-04-16 20:16:21 +000064 gophercloud.Result
65}
66
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010067// GetPasswordResult represent the result of a get os-server-password operation.
68type GetPasswordResult struct {
69 gophercloud.Result
70}
71
72// ExtractPassword gets the encrypted password.
73// If privateKey != nil the password is decrypted with the private key.
74// If privateKey == nil the encrypted password is returned and can be decrypted with:
75// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key>
76func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) {
jrperritt6a4dcc72016-07-21 18:59:43 -050077 var s struct {
78 Password string `json:"password"`
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010079 }
jrperritt6a4dcc72016-07-21 18:59:43 -050080 err := r.ExtractInto(&s)
81 if err == nil && privateKey != nil && s.Password != "" {
82 return decryptPassword(s.Password, privateKey)
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010083 }
jrperritt6a4dcc72016-07-21 18:59:43 -050084 return s.Password, err
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010085}
86
87func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) {
88 b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword)))
89
90 n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword))
91 if err != nil {
92 return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err)
93 }
94 password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n])
95 if err != nil {
96 return "", fmt.Errorf("Failed to decrypt password: %s", err)
97 }
98
99 return string(password), nil
100}
101
einarf4e5fdaf2015-04-16 23:14:59 +0000102// ExtractImageID gets the ID of the newly created server image from the header
103func (res CreateImageResult) ExtractImageID() (string, error) {
104 if res.Err != nil {
105 return "", res.Err
106 }
107 // Get the image id from the header
108 u, err := url.ParseRequestURI(res.Header.Get("Location"))
109 if err != nil {
Jon Perritt13808262016-03-09 00:50:12 -0600110 return "", err
einarf4e5fdaf2015-04-16 23:14:59 +0000111 }
Jon Perritt12395212016-02-24 10:41:17 -0600112 imageID := path.Base(u.Path)
113 if imageID == "." || imageID == "/" {
einarf4e5fdaf2015-04-16 23:14:59 +0000114 return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u)
115 }
Jon Perritt12395212016-02-24 10:41:17 -0600116 return imageID, nil
einarf4e5fdaf2015-04-16 23:14:59 +0000117}
118
Jon Perrittcc77da62014-11-16 13:14:21 -0700119// Extract interprets any RescueResult as an AdminPass, if possible.
Alex Gaynor7f3b06e2014-11-13 09:54:03 -0800120func (r RescueResult) Extract() (string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600121 var s struct {
122 AdminPass string `json:"adminPass"`
Alex Gaynor7f3b06e2014-11-13 09:54:03 -0800123 }
Jon Perritt12395212016-02-24 10:41:17 -0600124 err := r.ExtractInto(&s)
125 return s.AdminPass, err
Alex Gaynorfbe61bb2014-11-12 13:35:03 -0800126}
127
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800128// 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 -0800129type Server struct {
Ash Wilson01626a32014-09-17 10:38:07 -0400130 // ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
Jon Perritt12395212016-02-24 10:41:17 -0600131 ID string `json:"id"`
Ash Wilson01626a32014-09-17 10:38:07 -0400132
133 // TenantID identifies the tenant owning this server resource.
Jon Perritt12395212016-02-24 10:41:17 -0600134 TenantID string `json:"tenant_id"`
Ash Wilson01626a32014-09-17 10:38:07 -0400135
136 // UserID uniquely identifies the user account owning the tenant.
Jon Perritt12395212016-02-24 10:41:17 -0600137 UserID string `json:"user_id"`
Ash Wilson01626a32014-09-17 10:38:07 -0400138
139 // Name contains the human-readable name for the server.
Jon Perritt12395212016-02-24 10:41:17 -0600140 Name string `json:"name"`
Ash Wilson01626a32014-09-17 10:38:07 -0400141
142 // Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
143 Updated string
144 Created string
145
146 HostID string
147
148 // Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
149 Status string
150
151 // Progress ranges from 0..100.
152 // A request made against the server completes only once Progress reaches 100.
153 Progress int
154
155 // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
Jamie Hannaford908e1e92014-10-27 14:41:17 +0100156 AccessIPv4, AccessIPv6 string
Ash Wilson01626a32014-09-17 10:38:07 -0400157
158 // Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
159 Image map[string]interface{}
160
161 // Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
162 Flavor map[string]interface{}
163
164 // Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
Ash Wilson01626a32014-09-17 10:38:07 -0400165 Addresses map[string]interface{}
166
167 // Metadata includes a list of all user-specified key-value pairs attached to the server.
168 Metadata map[string]interface{}
169
170 // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
171 Links []interface{}
172
Ash Wilsonad21c712014-09-25 10:15:22 -0400173 // KeyName indicates which public key was injected into the server on launch.
Jon Perritt12395212016-02-24 10:41:17 -0600174 KeyName string `json:"key_name"`
Ash Wilsonad21c712014-09-25 10:15:22 -0400175
Ash Wilson01626a32014-09-17 10:38:07 -0400176 // 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.
177 // Note that this is the ONLY time this field will be valid.
Jon Perritt12395212016-02-24 10:41:17 -0600178 AdminPass string `json:"adminPass"`
Joe Topjian978bb502015-02-12 20:55:31 +0000179
180 // SecurityGroups includes the security groups that this instance has applied to it
Jon Perritt12395212016-02-24 10:41:17 -0600181 SecurityGroups []map[string]interface{} `json:"security_groups"`
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800182}
183
Ash Wilson397c78b2014-09-25 15:19:14 -0400184// ServerPage abstracts the raw results of making a List() request against the API.
185// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
186// data provided through the ExtractServers call.
187type ServerPage struct {
188 pagination.LinkedPageBase
Ash Wilsonf57381e2014-09-25 13:21:34 -0400189}
190
Ash Wilson397c78b2014-09-25 15:19:14 -0400191// IsEmpty returns true if a page contains no Server results.
192func (page ServerPage) IsEmpty() (bool, error) {
193 servers, err := ExtractServers(page)
Jon Perritt12395212016-02-24 10:41:17 -0600194 return len(servers) == 0, err
Ash Wilson397c78b2014-09-25 15:19:14 -0400195}
196
197// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
198func (page ServerPage) NextPageURL() (string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600199 var s struct {
200 Links []gophercloud.Link `json:"servers_links"`
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400201 }
Jon Perritt12395212016-02-24 10:41:17 -0600202 err := page.ExtractInto(&s)
Ash Wilson397c78b2014-09-25 15:19:14 -0400203 if err != nil {
204 return "", err
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400205 }
Jon Perritt12395212016-02-24 10:41:17 -0600206 return gophercloud.ExtractNextURL(s.Links)
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400207}
208
Ash Wilson01626a32014-09-17 10:38:07 -0400209// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
Jon Perritt31b66462016-02-25 22:25:30 -0600210func ExtractServers(r pagination.Page) ([]Server, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600211 var s struct {
212 Servers []Server `json:"servers"`
Ash Wilson12259392014-09-17 10:50:02 -0400213 }
Jon Perritt31b66462016-02-25 22:25:30 -0600214 err := (r.(ServerPage)).ExtractInto(&s)
Jon Perritt12395212016-02-24 10:41:17 -0600215 return s.Servers, err
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800216}
Jon Perrittcc77da62014-11-16 13:14:21 -0700217
Jon Perritt78c57ce2014-11-20 11:07:18 -0700218// MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
Jon Perrittcc77da62014-11-16 13:14:21 -0700219type MetadataResult struct {
220 gophercloud.Result
221}
222
223// GetMetadataResult temporarily contains the response from a metadata Get call.
224type GetMetadataResult struct {
225 MetadataResult
226}
227
Jon Perritt789f8322014-11-21 08:20:04 -0700228// ResetMetadataResult temporarily contains the response from a metadata Reset call.
229type ResetMetadataResult struct {
Jon Perrittcc77da62014-11-16 13:14:21 -0700230 MetadataResult
231}
232
Jon Perritt78c57ce2014-11-20 11:07:18 -0700233// UpdateMetadataResult temporarily contains the response from a metadata Update call.
234type UpdateMetadataResult struct {
235 MetadataResult
Jon Perrittcc77da62014-11-16 13:14:21 -0700236}
237
Jon Perritt78c57ce2014-11-20 11:07:18 -0700238// MetadatumResult contains the result of a call for individual a single key-value pair.
239type MetadatumResult struct {
240 gophercloud.Result
241}
Jon Perrittcc77da62014-11-16 13:14:21 -0700242
Jon Perritt78c57ce2014-11-20 11:07:18 -0700243// GetMetadatumResult temporarily contains the response from a metadatum Get call.
244type GetMetadatumResult struct {
245 MetadatumResult
246}
Jon Perrittcc77da62014-11-16 13:14:21 -0700247
Jon Perritt78c57ce2014-11-20 11:07:18 -0700248// CreateMetadatumResult temporarily contains the response from a metadatum Create call.
249type CreateMetadatumResult struct {
250 MetadatumResult
251}
252
253// DeleteMetadatumResult temporarily contains the response from a metadatum Delete call.
254type DeleteMetadatumResult struct {
255 gophercloud.ErrResult
Jon Perrittcc77da62014-11-16 13:14:21 -0700256}
257
258// Extract interprets any MetadataResult as a Metadata, if possible.
259func (r MetadataResult) Extract() (map[string]string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600260 var s struct {
261 Metadata map[string]string `json:"metadata"`
Jon Perrittcc77da62014-11-16 13:14:21 -0700262 }
Jon Perritt12395212016-02-24 10:41:17 -0600263 err := r.ExtractInto(&s)
264 return s.Metadata, err
Jon Perrittcc77da62014-11-16 13:14:21 -0700265}
Jon Perritt78c57ce2014-11-20 11:07:18 -0700266
267// Extract interprets any MetadatumResult as a Metadatum, if possible.
268func (r MetadatumResult) Extract() (map[string]string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600269 var s struct {
270 Metadatum map[string]string `json:"meta"`
Jon Perritt78c57ce2014-11-20 11:07:18 -0700271 }
Jon Perritt12395212016-02-24 10:41:17 -0600272 err := r.ExtractInto(&s)
273 return s.Metadatum, err
Jon Perritt0e19f602015-01-15 09:35:46 -0700274}
Jon Perritt5cb49482015-02-19 12:19:58 -0700275
276// Address represents an IP address.
277type Address struct {
Jon Perritt12395212016-02-24 10:41:17 -0600278 Version int `json:"version"`
279 Address string `json:"addr"`
Jon Perritt5cb49482015-02-19 12:19:58 -0700280}
281
Jon Perritt04d073c2015-02-19 21:46:23 -0700282// AddressPage abstracts the raw results of making a ListAddresses() request against the API.
Jon Perritt5cb49482015-02-19 12:19:58 -0700283// As OpenStack extensions may freely alter the response bodies of structures returned
284// to the client, you may only safely access the data provided through the ExtractAddresses call.
285type AddressPage struct {
286 pagination.SinglePageBase
287}
288
Jon Perritt04d073c2015-02-19 21:46:23 -0700289// IsEmpty returns true if an AddressPage contains no networks.
Jon Perritt5cb49482015-02-19 12:19:58 -0700290func (r AddressPage) IsEmpty() (bool, error) {
291 addresses, err := ExtractAddresses(r)
Jon Perritt12395212016-02-24 10:41:17 -0600292 return len(addresses) == 0, err
Jon Perritt5cb49482015-02-19 12:19:58 -0700293}
294
Jon Perritt04d073c2015-02-19 21:46:23 -0700295// ExtractAddresses interprets the results of a single page from a ListAddresses() call,
Jon Perritt5cb49482015-02-19 12:19:58 -0700296// producing a map of addresses.
Jon Perritt31b66462016-02-25 22:25:30 -0600297func ExtractAddresses(r pagination.Page) (map[string][]Address, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600298 var s struct {
299 Addresses map[string][]Address `json:"addresses"`
Jon Perritt5cb49482015-02-19 12:19:58 -0700300 }
Jon Perritt31b66462016-02-25 22:25:30 -0600301 err := (r.(AddressPage)).ExtractInto(&s)
Jon Perritt12395212016-02-24 10:41:17 -0600302 return s.Addresses, err
Jon Perritt5cb49482015-02-19 12:19:58 -0700303}
Jon Perritt04d073c2015-02-19 21:46:23 -0700304
305// NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() request against the API.
306// As OpenStack extensions may freely alter the response bodies of structures returned
307// to the client, you may only safely access the data provided through the ExtractAddresses call.
308type NetworkAddressPage struct {
309 pagination.SinglePageBase
310}
311
312// IsEmpty returns true if a NetworkAddressPage contains no addresses.
313func (r NetworkAddressPage) IsEmpty() (bool, error) {
314 addresses, err := ExtractNetworkAddresses(r)
Jon Perritt12395212016-02-24 10:41:17 -0600315 return len(addresses) == 0, err
Jon Perritt04d073c2015-02-19 21:46:23 -0700316}
317
318// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call,
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700319// producing a slice of addresses.
Jon Perritt31b66462016-02-25 22:25:30 -0600320func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600321 var s map[string][]Address
Jon Perritt31b66462016-02-25 22:25:30 -0600322 err := (r.(NetworkAddressPage)).ExtractInto(&s)
Jon Perritt04d073c2015-02-19 21:46:23 -0700323 if err != nil {
324 return nil, err
325 }
326
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700327 var key string
Jon Perritt12395212016-02-24 10:41:17 -0600328 for k := range s {
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700329 key = k
330 }
331
Jon Perritt12395212016-02-24 10:41:17 -0600332 return s[key], err
Jon Perritt04d073c2015-02-19 21:46:23 -0700333}