blob: 9215dcfdaa40861bbb89bdc8b5a6e8dac214d955 [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"
jrperritt98d01622017-01-12 14:24:42 -060010 "time"
Jon Perritt9a0980e2015-01-14 21:29:44 -070011
Krzysztof Szukiełojć3f41d082017-05-07 14:43:06 +020012 "gerrit.mcp.mirantis.net/debian/gophercloud.git"
Krzysztof Szukiełojć24a29ce2017-05-07 14:24:02 +020013 "gerrit.mcp.mirantis.net/debian/gophercloud.git/pagination"
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080014)
15
Ash Wilson397c78b2014-09-25 15:19:14 -040016type serverResult struct {
Ash Wilsonf548aad2014-10-20 08:35:34 -040017 gophercloud.Result
Ash Wilson397c78b2014-09-25 15:19:14 -040018}
19
20// Extract interprets any serverResult as a Server, if possible.
21func (r serverResult) Extract() (*Server, error) {
Joe Topjian0f64da02017-03-09 18:59:53 -070022 var s Server
Jon Perritt12395212016-02-24 10:41:17 -060023 err := r.ExtractInto(&s)
Joe Topjian0f64da02017-03-09 18:59:53 -070024 return &s, err
25}
26
27func (r serverResult) ExtractInto(v interface{}) error {
28 return r.Result.ExtractIntoStructPtr(v, "server")
29}
30
31func ExtractServersInto(r pagination.Page, v interface{}) error {
32 return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers")
Ash Wilson397c78b2014-09-25 15:19:14 -040033}
34
35// CreateResult temporarily contains the response from a Create call.
36type CreateResult struct {
37 serverResult
38}
39
40// GetResult temporarily contains the response from a Get call.
41type GetResult struct {
42 serverResult
43}
44
45// UpdateResult temporarily contains the response from an Update call.
46type UpdateResult struct {
47 serverResult
48}
49
Jon Perrittcc77da62014-11-16 13:14:21 -070050// DeleteResult temporarily contains the response from a Delete call.
Jamie Hannaford34732fe2014-10-27 11:29:36 +010051type DeleteResult struct {
Jon Perrittba2395e2014-10-27 15:23:21 -050052 gophercloud.ErrResult
Jamie Hannaford34732fe2014-10-27 11:29:36 +010053}
54
Ash Wilson397c78b2014-09-25 15:19:14 -040055// RebuildResult temporarily contains the response from a Rebuild call.
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020056type RebuildResult struct {
Ash Wilson397c78b2014-09-25 15:19:14 -040057 serverResult
58}
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080059
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020060// ActionResult represents the result of server action operations, like reboot
61type ActionResult struct {
Jon Perrittba2395e2014-10-27 15:23:21 -050062 gophercloud.ErrResult
Jamie Hannaford01c1efe2014-10-16 15:08:59 +020063}
64
Alex Gaynor587e3e32014-11-13 10:39:09 -080065// RescueResult represents the result of a server rescue operation
Alex Gaynorfbe61bb2014-11-12 13:35:03 -080066type RescueResult struct {
67 ActionResult
Alex Gaynor7f3b06e2014-11-13 09:54:03 -080068}
69
einarf4e5fdaf2015-04-16 23:14:59 +000070// CreateImageResult represents the result of an image creation operation
71type CreateImageResult struct {
einarf2fc665e2015-04-16 20:16:21 +000072 gophercloud.Result
73}
74
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010075// GetPasswordResult represent the result of a get os-server-password operation.
76type GetPasswordResult struct {
77 gophercloud.Result
78}
79
80// ExtractPassword gets the encrypted password.
81// If privateKey != nil the password is decrypted with the private key.
82// If privateKey == nil the encrypted password is returned and can be decrypted with:
83// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key>
84func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) {
jrperritt6a4dcc72016-07-21 18:59:43 -050085 var s struct {
86 Password string `json:"password"`
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010087 }
jrperritt6a4dcc72016-07-21 18:59:43 -050088 err := r.ExtractInto(&s)
89 if err == nil && privateKey != nil && s.Password != "" {
90 return decryptPassword(s.Password, privateKey)
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010091 }
jrperritt6a4dcc72016-07-21 18:59:43 -050092 return s.Password, err
Rickard von Essen5b8bbff2016-02-16 07:48:20 +010093}
94
95func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) {
96 b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword)))
97
98 n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword))
99 if err != nil {
100 return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err)
101 }
102 password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n])
103 if err != nil {
104 return "", fmt.Errorf("Failed to decrypt password: %s", err)
105 }
106
107 return string(password), nil
108}
109
einarf4e5fdaf2015-04-16 23:14:59 +0000110// ExtractImageID gets the ID of the newly created server image from the header
jrperritt98d01622017-01-12 14:24:42 -0600111func (r CreateImageResult) ExtractImageID() (string, error) {
112 if r.Err != nil {
113 return "", r.Err
einarf4e5fdaf2015-04-16 23:14:59 +0000114 }
115 // Get the image id from the header
jrperritt98d01622017-01-12 14:24:42 -0600116 u, err := url.ParseRequestURI(r.Header.Get("Location"))
einarf4e5fdaf2015-04-16 23:14:59 +0000117 if err != nil {
Jon Perritt13808262016-03-09 00:50:12 -0600118 return "", err
einarf4e5fdaf2015-04-16 23:14:59 +0000119 }
Jon Perritt12395212016-02-24 10:41:17 -0600120 imageID := path.Base(u.Path)
121 if imageID == "." || imageID == "/" {
einarf4e5fdaf2015-04-16 23:14:59 +0000122 return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u)
123 }
Jon Perritt12395212016-02-24 10:41:17 -0600124 return imageID, nil
einarf4e5fdaf2015-04-16 23:14:59 +0000125}
126
Jon Perrittcc77da62014-11-16 13:14:21 -0700127// Extract interprets any RescueResult as an AdminPass, if possible.
Alex Gaynor7f3b06e2014-11-13 09:54:03 -0800128func (r RescueResult) Extract() (string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600129 var s struct {
130 AdminPass string `json:"adminPass"`
Alex Gaynor7f3b06e2014-11-13 09:54:03 -0800131 }
Jon Perritt12395212016-02-24 10:41:17 -0600132 err := r.ExtractInto(&s)
133 return s.AdminPass, err
Alex Gaynorfbe61bb2014-11-12 13:35:03 -0800134}
135
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800136// 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 -0800137type Server struct {
Ash Wilson01626a32014-09-17 10:38:07 -0400138 // ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
Jon Perritt12395212016-02-24 10:41:17 -0600139 ID string `json:"id"`
Ash Wilson01626a32014-09-17 10:38:07 -0400140 // TenantID identifies the tenant owning this server resource.
Jon Perritt12395212016-02-24 10:41:17 -0600141 TenantID string `json:"tenant_id"`
Ash Wilson01626a32014-09-17 10:38:07 -0400142 // UserID uniquely identifies the user account owning the tenant.
Jon Perritt12395212016-02-24 10:41:17 -0600143 UserID string `json:"user_id"`
Ash Wilson01626a32014-09-17 10:38:07 -0400144 // Name contains the human-readable name for the server.
Jon Perritt12395212016-02-24 10:41:17 -0600145 Name string `json:"name"`
Ash Wilson01626a32014-09-17 10:38:07 -0400146 // Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
jrperritt98d01622017-01-12 14:24:42 -0600147 Updated time.Time `json:"updated"`
148 Created time.Time `json:"created"`
149 HostID string `json:"hostid"`
Ash Wilson01626a32014-09-17 10:38:07 -0400150 // Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
jrperritt98d01622017-01-12 14:24:42 -0600151 Status string `json:"status"`
Ash Wilson01626a32014-09-17 10:38:07 -0400152 // Progress ranges from 0..100.
153 // A request made against the server completes only once Progress reaches 100.
jrperritt98d01622017-01-12 14:24:42 -0600154 Progress int `json:"progress"`
Ash Wilson01626a32014-09-17 10:38:07 -0400155 // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
jrperritt98d01622017-01-12 14:24:42 -0600156 AccessIPv4 string `json:"accessIPv4"`
157 AccessIPv6 string `json:"accessIPv6"`
Ash Wilson01626a32014-09-17 10:38:07 -0400158 // Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
jrperritt98d01622017-01-12 14:24:42 -0600159 Image map[string]interface{} `json:"-"`
Ash Wilson01626a32014-09-17 10:38:07 -0400160 // Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
jrperritt98d01622017-01-12 14:24:42 -0600161 Flavor map[string]interface{} `json:"flavor"`
Ash Wilson01626a32014-09-17 10:38:07 -0400162 // Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
jrperritt98d01622017-01-12 14:24:42 -0600163 Addresses map[string]interface{} `json:"addresses"`
Ash Wilson01626a32014-09-17 10:38:07 -0400164 // Metadata includes a list of all user-specified key-value pairs attached to the server.
jrperritt98d01622017-01-12 14:24:42 -0600165 Metadata map[string]string `json:"metadata"`
Ash Wilson01626a32014-09-17 10:38:07 -0400166 // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
jrperritt98d01622017-01-12 14:24:42 -0600167 Links []interface{} `json:"links"`
Ash Wilsonad21c712014-09-25 10:15:22 -0400168 // KeyName indicates which public key was injected into the server on launch.
Jon Perritt12395212016-02-24 10:41:17 -0600169 KeyName string `json:"key_name"`
Ash Wilson01626a32014-09-17 10:38:07 -0400170 // 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.
171 // Note that this is the ONLY time this field will be valid.
Jon Perritt12395212016-02-24 10:41:17 -0600172 AdminPass string `json:"adminPass"`
Joe Topjian978bb502015-02-12 20:55:31 +0000173 // SecurityGroups includes the security groups that this instance has applied to it
Jon Perritt12395212016-02-24 10:41:17 -0600174 SecurityGroups []map[string]interface{} `json:"security_groups"`
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800175}
176
jrperritt98d01622017-01-12 14:24:42 -0600177func (r *Server) UnmarshalJSON(b []byte) error {
jrperritt1fa92502016-07-21 19:22:59 -0500178 type tmp Server
jrperritt98d01622017-01-12 14:24:42 -0600179 var s struct {
jrperritt1fa92502016-07-21 19:22:59 -0500180 tmp
jrperritt98d01622017-01-12 14:24:42 -0600181 Image interface{} `json:"image"`
jrperritt1fa92502016-07-21 19:22:59 -0500182 }
jrperritt98d01622017-01-12 14:24:42 -0600183 err := json.Unmarshal(b, &s)
jrperritt1fa92502016-07-21 19:22:59 -0500184 if err != nil {
185 return err
186 }
187
jrperritt98d01622017-01-12 14:24:42 -0600188 *r = Server(s.tmp)
jrperritt1fa92502016-07-21 19:22:59 -0500189
jrperritt98d01622017-01-12 14:24:42 -0600190 switch t := s.Image.(type) {
jrperritt1fa92502016-07-21 19:22:59 -0500191 case map[string]interface{}:
jrperritt98d01622017-01-12 14:24:42 -0600192 r.Image = t
jrperritt1fa92502016-07-21 19:22:59 -0500193 case string:
194 switch t {
195 case "":
jrperritt98d01622017-01-12 14:24:42 -0600196 r.Image = nil
jrperritt1fa92502016-07-21 19:22:59 -0500197 }
198 }
199
jrperritt98d01622017-01-12 14:24:42 -0600200 return err
jrperritt1fa92502016-07-21 19:22:59 -0500201}
202
Ash Wilson397c78b2014-09-25 15:19:14 -0400203// ServerPage abstracts the raw results of making a List() request against the API.
204// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
205// data provided through the ExtractServers call.
206type ServerPage struct {
207 pagination.LinkedPageBase
Ash Wilsonf57381e2014-09-25 13:21:34 -0400208}
209
Ash Wilson397c78b2014-09-25 15:19:14 -0400210// IsEmpty returns true if a page contains no Server results.
jrperritt98d01622017-01-12 14:24:42 -0600211func (r ServerPage) IsEmpty() (bool, error) {
212 s, err := ExtractServers(r)
213 return len(s) == 0, err
Ash Wilson397c78b2014-09-25 15:19:14 -0400214}
215
216// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
jrperritt98d01622017-01-12 14:24:42 -0600217func (r ServerPage) NextPageURL() (string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600218 var s struct {
219 Links []gophercloud.Link `json:"servers_links"`
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400220 }
jrperritt98d01622017-01-12 14:24:42 -0600221 err := r.ExtractInto(&s)
Ash Wilson397c78b2014-09-25 15:19:14 -0400222 if err != nil {
223 return "", err
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400224 }
Jon Perritt12395212016-02-24 10:41:17 -0600225 return gophercloud.ExtractNextURL(s.Links)
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400226}
227
Ash Wilson01626a32014-09-17 10:38:07 -0400228// 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 -0600229func ExtractServers(r pagination.Page) ([]Server, error) {
Joe Topjian0f64da02017-03-09 18:59:53 -0700230 var s []Server
231 err := ExtractServersInto(r, &s)
232 return s, err
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -0800233}
Jon Perrittcc77da62014-11-16 13:14:21 -0700234
Ildar Svetlov6c31b972018-03-12 17:01:52 +0400235// ShortServer exposes only IDs, names, and links corresponding to a given server on the user's account.
236type ShortServer struct {
237 // ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant.
238 ID string `json:"id"`
239 // Name contains the human-readable name for the server.
240 Name string `json:"name"`
241 // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
242 Links []interface{} `json:"links"`
243}
244
245// ShortServerPage abstracts the raw results of making a ListServers() request against the API.
246type ShortServerPage struct {
247 pagination.SinglePageBase
248}
249
250// IsEmpty returns true if a page contains no ShortServer results.
251func (r ShortServerPage) IsEmpty() (bool, error) {
252 s, err := ExtractShortServers(r)
253 return len(s) == 0, err
254}
255
256// ExtractShortServers interprets the results of a single page from a ListServers() call, producing a slice of ShortServer entities.
257func ExtractShortServers(r pagination.Page) ([]ShortServer, error) {
258 var s struct {
259 ShortServers []ShortServer `json:"servers"`
260 }
261 err := (r.(ShortServerPage)).ExtractInto(&s)
262 return s.ShortServers, err
263}
264
Jon Perritt78c57ce2014-11-20 11:07:18 -0700265// MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
Jon Perrittcc77da62014-11-16 13:14:21 -0700266type MetadataResult struct {
267 gophercloud.Result
268}
269
270// GetMetadataResult temporarily contains the response from a metadata Get call.
271type GetMetadataResult struct {
272 MetadataResult
273}
274
Jon Perritt789f8322014-11-21 08:20:04 -0700275// ResetMetadataResult temporarily contains the response from a metadata Reset call.
276type ResetMetadataResult struct {
Jon Perrittcc77da62014-11-16 13:14:21 -0700277 MetadataResult
278}
279
Jon Perritt78c57ce2014-11-20 11:07:18 -0700280// UpdateMetadataResult temporarily contains the response from a metadata Update call.
281type UpdateMetadataResult struct {
282 MetadataResult
Jon Perrittcc77da62014-11-16 13:14:21 -0700283}
284
Jon Perritt78c57ce2014-11-20 11:07:18 -0700285// MetadatumResult contains the result of a call for individual a single key-value pair.
286type MetadatumResult struct {
287 gophercloud.Result
288}
Jon Perrittcc77da62014-11-16 13:14:21 -0700289
Jon Perritt78c57ce2014-11-20 11:07:18 -0700290// GetMetadatumResult temporarily contains the response from a metadatum Get call.
291type GetMetadatumResult struct {
292 MetadatumResult
293}
Jon Perrittcc77da62014-11-16 13:14:21 -0700294
Jon Perritt78c57ce2014-11-20 11:07:18 -0700295// CreateMetadatumResult temporarily contains the response from a metadatum Create call.
296type CreateMetadatumResult struct {
297 MetadatumResult
298}
299
300// DeleteMetadatumResult temporarily contains the response from a metadatum Delete call.
301type DeleteMetadatumResult struct {
302 gophercloud.ErrResult
Jon Perrittcc77da62014-11-16 13:14:21 -0700303}
304
305// Extract interprets any MetadataResult as a Metadata, if possible.
306func (r MetadataResult) Extract() (map[string]string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600307 var s struct {
308 Metadata map[string]string `json:"metadata"`
Jon Perrittcc77da62014-11-16 13:14:21 -0700309 }
Jon Perritt12395212016-02-24 10:41:17 -0600310 err := r.ExtractInto(&s)
311 return s.Metadata, err
Jon Perrittcc77da62014-11-16 13:14:21 -0700312}
Jon Perritt78c57ce2014-11-20 11:07:18 -0700313
314// Extract interprets any MetadatumResult as a Metadatum, if possible.
315func (r MetadatumResult) Extract() (map[string]string, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600316 var s struct {
317 Metadatum map[string]string `json:"meta"`
Jon Perritt78c57ce2014-11-20 11:07:18 -0700318 }
Jon Perritt12395212016-02-24 10:41:17 -0600319 err := r.ExtractInto(&s)
320 return s.Metadatum, err
Jon Perritt0e19f602015-01-15 09:35:46 -0700321}
Jon Perritt5cb49482015-02-19 12:19:58 -0700322
323// Address represents an IP address.
324type Address struct {
Jon Perritt12395212016-02-24 10:41:17 -0600325 Version int `json:"version"`
326 Address string `json:"addr"`
Jon Perritt5cb49482015-02-19 12:19:58 -0700327}
328
Jon Perritt04d073c2015-02-19 21:46:23 -0700329// AddressPage abstracts the raw results of making a ListAddresses() request against the API.
Jon Perritt5cb49482015-02-19 12:19:58 -0700330// As OpenStack extensions may freely alter the response bodies of structures returned
331// to the client, you may only safely access the data provided through the ExtractAddresses call.
332type AddressPage struct {
333 pagination.SinglePageBase
334}
335
Jon Perritt04d073c2015-02-19 21:46:23 -0700336// IsEmpty returns true if an AddressPage contains no networks.
Jon Perritt5cb49482015-02-19 12:19:58 -0700337func (r AddressPage) IsEmpty() (bool, error) {
338 addresses, err := ExtractAddresses(r)
Jon Perritt12395212016-02-24 10:41:17 -0600339 return len(addresses) == 0, err
Jon Perritt5cb49482015-02-19 12:19:58 -0700340}
341
Jon Perritt04d073c2015-02-19 21:46:23 -0700342// ExtractAddresses interprets the results of a single page from a ListAddresses() call,
Jon Perritt5cb49482015-02-19 12:19:58 -0700343// producing a map of addresses.
Jon Perritt31b66462016-02-25 22:25:30 -0600344func ExtractAddresses(r pagination.Page) (map[string][]Address, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600345 var s struct {
346 Addresses map[string][]Address `json:"addresses"`
Jon Perritt5cb49482015-02-19 12:19:58 -0700347 }
Jon Perritt31b66462016-02-25 22:25:30 -0600348 err := (r.(AddressPage)).ExtractInto(&s)
Jon Perritt12395212016-02-24 10:41:17 -0600349 return s.Addresses, err
Jon Perritt5cb49482015-02-19 12:19:58 -0700350}
Jon Perritt04d073c2015-02-19 21:46:23 -0700351
352// NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() request against the API.
353// As OpenStack extensions may freely alter the response bodies of structures returned
354// to the client, you may only safely access the data provided through the ExtractAddresses call.
355type NetworkAddressPage struct {
356 pagination.SinglePageBase
357}
358
359// IsEmpty returns true if a NetworkAddressPage contains no addresses.
360func (r NetworkAddressPage) IsEmpty() (bool, error) {
361 addresses, err := ExtractNetworkAddresses(r)
Jon Perritt12395212016-02-24 10:41:17 -0600362 return len(addresses) == 0, err
Jon Perritt04d073c2015-02-19 21:46:23 -0700363}
364
365// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call,
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700366// producing a slice of addresses.
Jon Perritt31b66462016-02-25 22:25:30 -0600367func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) {
Jon Perritt12395212016-02-24 10:41:17 -0600368 var s map[string][]Address
Jon Perritt31b66462016-02-25 22:25:30 -0600369 err := (r.(NetworkAddressPage)).ExtractInto(&s)
Jon Perritt04d073c2015-02-19 21:46:23 -0700370 if err != nil {
371 return nil, err
372 }
373
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700374 var key string
Jon Perritt12395212016-02-24 10:41:17 -0600375 for k := range s {
Jon Perrittb51ba9c2015-02-23 10:56:35 -0700376 key = k
377 }
378
Jon Perritt12395212016-02-24 10:41:17 -0600379 return s[key], err
Jon Perritt04d073c2015-02-19 21:46:23 -0700380}