blob: 7659c75c104455c1d0cf55655eca99f481165fe0 [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"
jrperritt1fa92502016-07-21 19:22:59 -05006 "encoding/json"
einarf4e5fdaf2015-04-16 23:14:59 +00007 "fmt"
einarf4e5fdaf2015-04-16 23:14:59 +00008 "net/url"
Jon Perritt12395212016-02-24 10:41:17 -06009 "path"
Jon Perritt9a0980e2015-01-14 21:29:44 -070010
Jon Perritt27249f42016-02-18 10:35:59 -060011 "github.com/gophercloud/gophercloud"
12 "github.com/gophercloud/gophercloud/pagination"
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080013)
14
Ash Wilson397c78b2014-09-25 15:19:14 -040015type serverResult struct {
Ash Wilsonf548aad2014-10-20 08:35:34 -040016 gophercloud.Result
Ash Wilson397c78b2014-09-25 15:19:14 -040017}
18
19// Extract interprets any serverResult as a Server, if possible.
20func (r serverResult) Extract() (*Server, error) {
Jon Perritt12395212016-02-24 10:41:17 -060021 var s struct {
22 Server *Server `json:"server"`
Ash Wilson397c78b2014-09-25 15:19:14 -040023 }
Jon Perritt12395212016-02-24 10:41:17 -060024 err := r.ExtractInto(&s)
25 return s.Server, err
Ash Wilson397c78b2014-09-25 15:19:14 -040026}
27
28// CreateResult temporarily contains the response from a Create call.
29type CreateResult struct {
30 serverResult
31}
32
33// GetResult temporarily contains the response from a Get call.
34type GetResult struct {
35 serverResult
36}
37
38// UpdateResult temporarily contains the response from an Update call.
39type UpdateResult struct {
40 serverResult
41}
42
Jon Perrittcc77da62014-11-16 13:14:21 -070043// DeleteResult temporarily contains the response from a Delete call.
Jamie Hannaford34732fe2014-10-27 11:29:36 +010044type DeleteResult struct {
Jon Perrittba2395e2014-10-27 15:23:21 -050045 gophercloud.ErrResult
Jamie Hannaford34732fe2014-10-27 11:29:36 +010046}
47
Ash Wilson397c78b2014-09-25 15:19:14 -040048// RebuildResult temporarily contains the response from a Rebuild call.
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020049type RebuildResult struct {
Ash Wilson397c78b2014-09-25 15:19:14 -040050 serverResult
51}
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080052
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020053// ActionResult represents the result of server action operations, like reboot
54type ActionResult struct {
Jon Perrittba2395e2014-10-27 15:23:21 -050055 gophercloud.ErrResult
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020056}
57
Alex Gaynor587e3e32014-11-13 10:39:09 -080058// RescueResult represents the result of a server rescue operation
Alex Gaynorfbe61bb2014-11-12 13:35:03 -080059type RescueResult struct {
60 ActionResult
Alex Gaynor7f3b06e2014-11-13 09:54:03 -080061}
62
einarf4e5fdaf2015-04-16 23:14:59 +000063// CreateImageResult represents the result of an image creation operation
64type CreateImageResult struct {
einarf2fc665e2015-04-16 20:16:21 +000065 gophercloud.Result
66}
67
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010068// GetPasswordResult represent the result of a get os-server-password operation.
69type GetPasswordResult struct {
70 gophercloud.Result
71}
72
73// ExtractPassword gets the encrypted password.
74// If privateKey != nil the password is decrypted with the private key.
75// If privateKey == nil the encrypted password is returned and can be decrypted with:
76// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key>
77func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) {
jrperritt6a4dcc72016-07-21 18:59:43 -050078 var s struct {
79 Password string `json:"password"`
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010080 }
jrperritt6a4dcc72016-07-21 18:59:43 -050081 err := r.ExtractInto(&s)
82 if err == nil && privateKey != nil && s.Password != "" {
83 return decryptPassword(s.Password, privateKey)
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010084 }
jrperritt6a4dcc72016-07-21 18:59:43 -050085 return s.Password, err
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010086}
87
88func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) {
89 b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword)))
90
91 n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword))
92 if err != nil {
93 return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err)
94 }
95 password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n])
96 if err != nil {
97 return "", fmt.Errorf("Failed to decrypt password: %s", err)
98 }
99
100 return string(password), nil
101}
102
einarf4e5fdaf2015-04-16 23:14:59 +0000103// ExtractImageID gets the ID of the newly created server image from the header
104func (res CreateImageResult) ExtractImageID() (string, error) {
105 if res.Err != nil {
106 return "", res.Err
107 }
108 // Get the image id from the header
109 u, err := url.ParseRequestURI(res.Header.Get("Location"))
110 if err != nil {
Jon Perritt13808262016-03-09 00:50:12 -0600111 return "", err
einarf4e5fdaf2015-04-16 23:14:59 +0000112 }
Jon Perritt12395212016-02-24 10:41:17 -0600113 imageID := path.Base(u.Path)
114 if imageID == "." || imageID == "/" {
einarf4e5fdaf2015-04-16 23:14:59 +0000115 return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u)
116 }
Jon Perritt12395212016-02-24 10:41:17 -0600117 return imageID, nil
einarf4e5fdaf2015-04-16 23:14:59 +0000118}
119
Jon Perrittcc77da62014-11-16 13:14:21 -0700120// Extract interprets any RescueResult as an AdminPass, if possible.
Alex Gaynor7f3b06e2014-11-13 09:54:03 -0800121func (r RescueResult) Extract() (string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600122 var s struct {
123 AdminPass string `json:"adminPass"`
Alex Gaynor7f3b06e2014-11-13 09:54:03 -0800124 }
Jon Perritt12395212016-02-24 10:41:17 -0600125 err := r.ExtractInto(&s)
126 return s.AdminPass, err
Alex Gaynorfbe61bb2014-11-12 13:35:03 -0800127}
128
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800129// 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 -0800130type Server struct {
Ash Wilson01626a32014-09-17 10:38:07 -0400131 // ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
Jon Perritt12395212016-02-24 10:41:17 -0600132 ID string `json:"id"`
Ash Wilson01626a32014-09-17 10:38:07 -0400133 // 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 // UserID uniquely identifies the user account owning the tenant.
Jon Perritt12395212016-02-24 10:41:17 -0600136 UserID string `json:"user_id"`
Ash Wilson01626a32014-09-17 10:38:07 -0400137 // Name contains the human-readable name for the server.
Jon Perritt12395212016-02-24 10:41:17 -0600138 Name string `json:"name"`
Ash Wilson01626a32014-09-17 10:38:07 -0400139 // Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
140 Updated string
141 Created string
jrperritt1fa92502016-07-21 19:22:59 -0500142 HostID string
Ash Wilson01626a32014-09-17 10:38:07 -0400143 // Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
144 Status string
Ash Wilson01626a32014-09-17 10:38:07 -0400145 // Progress ranges from 0..100.
146 // A request made against the server completes only once Progress reaches 100.
147 Progress int
Ash Wilson01626a32014-09-17 10:38:07 -0400148 // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
Jamie Hannaford908e1e92014-10-27 14:41:17 +0100149 AccessIPv4, AccessIPv6 string
Ash Wilson01626a32014-09-17 10:38:07 -0400150 // Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
151 Image map[string]interface{}
Ash Wilson01626a32014-09-17 10:38:07 -0400152 // Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
153 Flavor map[string]interface{}
Ash Wilson01626a32014-09-17 10:38:07 -0400154 // Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
Ash Wilson01626a32014-09-17 10:38:07 -0400155 Addresses map[string]interface{}
Ash Wilson01626a32014-09-17 10:38:07 -0400156 // Metadata includes a list of all user-specified key-value pairs attached to the server.
157 Metadata map[string]interface{}
Ash Wilson01626a32014-09-17 10:38:07 -0400158 // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
159 Links []interface{}
Ash Wilsonad21c712014-09-25 10:15:22 -0400160 // KeyName indicates which public key was injected into the server on launch.
Jon Perritt12395212016-02-24 10:41:17 -0600161 KeyName string `json:"key_name"`
Ash Wilson01626a32014-09-17 10:38:07 -0400162 // 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.
163 // Note that this is the ONLY time this field will be valid.
Jon Perritt12395212016-02-24 10:41:17 -0600164 AdminPass string `json:"adminPass"`
Joe Topjian978bb502015-02-12 20:55:31 +0000165 // SecurityGroups includes the security groups that this instance has applied to it
Jon Perritt12395212016-02-24 10:41:17 -0600166 SecurityGroups []map[string]interface{} `json:"security_groups"`
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800167}
168
jrperritt1fa92502016-07-21 19:22:59 -0500169func (s *Server) UnmarshalJSON(b []byte) error {
170 type tmp Server
171 var server *struct {
172 tmp
173 Image interface{}
174 }
175 err := json.Unmarshal(b, &server)
176 if err != nil {
177 return err
178 }
179
180 *s = Server(server.tmp)
181
182 switch t := server.Image.(type) {
183 case map[string]interface{}:
184 s.Image = t
185 case string:
186 switch t {
187 case "":
188 s.Image = nil
189 }
190 }
191
192 return nil
193}
194
Ash Wilson397c78b2014-09-25 15:19:14 -0400195// ServerPage abstracts the raw results of making a List() request against the API.
196// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
197// data provided through the ExtractServers call.
198type ServerPage struct {
199 pagination.LinkedPageBase
Ash Wilsonf57381e2014-09-25 13:21:34 -0400200}
201
Ash Wilson397c78b2014-09-25 15:19:14 -0400202// IsEmpty returns true if a page contains no Server results.
203func (page ServerPage) IsEmpty() (bool, error) {
204 servers, err := ExtractServers(page)
Jon Perritt12395212016-02-24 10:41:17 -0600205 return len(servers) == 0, err
Ash Wilson397c78b2014-09-25 15:19:14 -0400206}
207
208// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
209func (page ServerPage) NextPageURL() (string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600210 var s struct {
211 Links []gophercloud.Link `json:"servers_links"`
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400212 }
Jon Perritt12395212016-02-24 10:41:17 -0600213 err := page.ExtractInto(&s)
Ash Wilson397c78b2014-09-25 15:19:14 -0400214 if err != nil {
215 return "", err
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400216 }
Jon Perritt12395212016-02-24 10:41:17 -0600217 return gophercloud.ExtractNextURL(s.Links)
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400218}
219
Ash Wilson01626a32014-09-17 10:38:07 -0400220// 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 -0600221func ExtractServers(r pagination.Page) ([]Server, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600222 var s struct {
223 Servers []Server `json:"servers"`
Ash Wilson12259392014-09-17 10:50:02 -0400224 }
Jon Perritt31b66462016-02-25 22:25:30 -0600225 err := (r.(ServerPage)).ExtractInto(&s)
Jon Perritt12395212016-02-24 10:41:17 -0600226 return s.Servers, err
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800227}
Jon Perrittcc77da62014-11-16 13:14:21 -0700228
Jon Perritt78c57ce2014-11-20 11:07:18 -0700229// MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
Jon Perrittcc77da62014-11-16 13:14:21 -0700230type MetadataResult struct {
231 gophercloud.Result
232}
233
234// GetMetadataResult temporarily contains the response from a metadata Get call.
235type GetMetadataResult struct {
236 MetadataResult
237}
238
Jon Perritt789f8322014-11-21 08:20:04 -0700239// ResetMetadataResult temporarily contains the response from a metadata Reset call.
240type ResetMetadataResult struct {
Jon Perrittcc77da62014-11-16 13:14:21 -0700241 MetadataResult
242}
243
Jon Perritt78c57ce2014-11-20 11:07:18 -0700244// UpdateMetadataResult temporarily contains the response from a metadata Update call.
245type UpdateMetadataResult struct {
246 MetadataResult
Jon Perrittcc77da62014-11-16 13:14:21 -0700247}
248
Jon Perritt78c57ce2014-11-20 11:07:18 -0700249// MetadatumResult contains the result of a call for individual a single key-value pair.
250type MetadatumResult struct {
251 gophercloud.Result
252}
Jon Perrittcc77da62014-11-16 13:14:21 -0700253
Jon Perritt78c57ce2014-11-20 11:07:18 -0700254// GetMetadatumResult temporarily contains the response from a metadatum Get call.
255type GetMetadatumResult struct {
256 MetadatumResult
257}
Jon Perrittcc77da62014-11-16 13:14:21 -0700258
Jon Perritt78c57ce2014-11-20 11:07:18 -0700259// CreateMetadatumResult temporarily contains the response from a metadatum Create call.
260type CreateMetadatumResult struct {
261 MetadatumResult
262}
263
264// DeleteMetadatumResult temporarily contains the response from a metadatum Delete call.
265type DeleteMetadatumResult struct {
266 gophercloud.ErrResult
Jon Perrittcc77da62014-11-16 13:14:21 -0700267}
268
269// Extract interprets any MetadataResult as a Metadata, if possible.
270func (r MetadataResult) Extract() (map[string]string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600271 var s struct {
272 Metadata map[string]string `json:"metadata"`
Jon Perrittcc77da62014-11-16 13:14:21 -0700273 }
Jon Perritt12395212016-02-24 10:41:17 -0600274 err := r.ExtractInto(&s)
275 return s.Metadata, err
Jon Perrittcc77da62014-11-16 13:14:21 -0700276}
Jon Perritt78c57ce2014-11-20 11:07:18 -0700277
278// Extract interprets any MetadatumResult as a Metadatum, if possible.
279func (r MetadatumResult) Extract() (map[string]string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600280 var s struct {
281 Metadatum map[string]string `json:"meta"`
Jon Perritt78c57ce2014-11-20 11:07:18 -0700282 }
Jon Perritt12395212016-02-24 10:41:17 -0600283 err := r.ExtractInto(&s)
284 return s.Metadatum, err
Jon Perritt0e19f602015-01-15 09:35:46 -0700285}
Jon Perritt5cb49482015-02-19 12:19:58 -0700286
287// Address represents an IP address.
288type Address struct {
Jon Perritt12395212016-02-24 10:41:17 -0600289 Version int `json:"version"`
290 Address string `json:"addr"`
Jon Perritt5cb49482015-02-19 12:19:58 -0700291}
292
Jon Perritt04d073c2015-02-19 21:46:23 -0700293// AddressPage abstracts the raw results of making a ListAddresses() request against the API.
Jon Perritt5cb49482015-02-19 12:19:58 -0700294// As OpenStack extensions may freely alter the response bodies of structures returned
295// to the client, you may only safely access the data provided through the ExtractAddresses call.
296type AddressPage struct {
297 pagination.SinglePageBase
298}
299
Jon Perritt04d073c2015-02-19 21:46:23 -0700300// IsEmpty returns true if an AddressPage contains no networks.
Jon Perritt5cb49482015-02-19 12:19:58 -0700301func (r AddressPage) IsEmpty() (bool, error) {
302 addresses, err := ExtractAddresses(r)
Jon Perritt12395212016-02-24 10:41:17 -0600303 return len(addresses) == 0, err
Jon Perritt5cb49482015-02-19 12:19:58 -0700304}
305
Jon Perritt04d073c2015-02-19 21:46:23 -0700306// ExtractAddresses interprets the results of a single page from a ListAddresses() call,
Jon Perritt5cb49482015-02-19 12:19:58 -0700307// producing a map of addresses.
Jon Perritt31b66462016-02-25 22:25:30 -0600308func ExtractAddresses(r pagination.Page) (map[string][]Address, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600309 var s struct {
310 Addresses map[string][]Address `json:"addresses"`
Jon Perritt5cb49482015-02-19 12:19:58 -0700311 }
Jon Perritt31b66462016-02-25 22:25:30 -0600312 err := (r.(AddressPage)).ExtractInto(&s)
Jon Perritt12395212016-02-24 10:41:17 -0600313 return s.Addresses, err
Jon Perritt5cb49482015-02-19 12:19:58 -0700314}
Jon Perritt04d073c2015-02-19 21:46:23 -0700315
316// NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() request against the API.
317// As OpenStack extensions may freely alter the response bodies of structures returned
318// to the client, you may only safely access the data provided through the ExtractAddresses call.
319type NetworkAddressPage struct {
320 pagination.SinglePageBase
321}
322
323// IsEmpty returns true if a NetworkAddressPage contains no addresses.
324func (r NetworkAddressPage) IsEmpty() (bool, error) {
325 addresses, err := ExtractNetworkAddresses(r)
Jon Perritt12395212016-02-24 10:41:17 -0600326 return len(addresses) == 0, err
Jon Perritt04d073c2015-02-19 21:46:23 -0700327}
328
329// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call,
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700330// producing a slice of addresses.
Jon Perritt31b66462016-02-25 22:25:30 -0600331func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600332 var s map[string][]Address
Jon Perritt31b66462016-02-25 22:25:30 -0600333 err := (r.(NetworkAddressPage)).ExtractInto(&s)
Jon Perritt04d073c2015-02-19 21:46:23 -0700334 if err != nil {
335 return nil, err
336 }
337
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700338 var key string
Jon Perritt12395212016-02-24 10:41:17 -0600339 for k := range s {
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700340 key = k
341 }
342
Jon Perritt12395212016-02-24 10:41:17 -0600343 return s[key], err
Jon Perritt04d073c2015-02-19 21:46:23 -0700344}