blob: da93ff1f41c253a9fb5b4ad2e7285ccd30f920a2 [file] [log] [blame]
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -08001package servers
2
3import (
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -07004 "fmt"
Ash Wilson01626a32014-09-17 10:38:07 -04005
Jon Perritt30558642014-04-14 17:07:12 -05006 "github.com/racker/perigee"
Ash Wilson01626a32014-09-17 10:38:07 -04007 "github.com/rackspace/gophercloud"
8 "github.com/rackspace/gophercloud/pagination"
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -08009)
10
Ash Wilsonfd043792014-09-17 10:40:17 -040011// ListPage abstracts the raw results of making a List() request against the API.
Ash Wilson01626a32014-09-17 10:38:07 -040012// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
13// data provided through the ExtractServers call.
Ash Wilsonfd043792014-09-17 10:40:17 -040014type ListPage struct {
Ash Wilson01626a32014-09-17 10:38:07 -040015 pagination.MarkerPageBase
16}
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080017
Ash Wilson01626a32014-09-17 10:38:07 -040018// IsEmpty returns true if a page contains no Server results.
Ash Wilsonfd043792014-09-17 10:40:17 -040019func (page ListPage) IsEmpty() (bool, error) {
Ash Wilson01626a32014-09-17 10:38:07 -040020 servers, err := ExtractServers(page)
21 if err != nil {
22 return true, err
23 }
24 return len(servers) == 0, nil
25}
26
27// LastMarker returns the ID of the final server on the current page.
Ash Wilsonfd043792014-09-17 10:40:17 -040028func (page ListPage) LastMarker() (string, error) {
Ash Wilson01626a32014-09-17 10:38:07 -040029 servers, err := ExtractServers(page)
30 if err != nil {
31 return "", err
32 }
33 if len(servers) == 0 {
34 return "", nil
35 }
36 return servers[len(servers)-1].ID, nil
37}
38
39// ServerResult abstracts a single server description, as returned by the OpenStack provider.
40// As OpenStack extensions may freely alter the response bodies of the structures returned to the client,
41// you may only safely access the data provided through separate, type-safe accessors or methods.
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080042type ServerResult map[string]interface{}
43
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080044// List makes a request against the API to list servers accessible to you.
Ash Wilson01626a32014-09-17 10:38:07 -040045func List(client *gophercloud.ServiceClient) pagination.Pager {
46 createPage := func(r pagination.LastHTTPResponse) pagination.Page {
Ash Wilsonfd043792014-09-17 10:40:17 -040047 p := ListPage{pagination.MarkerPageBase{LastHTTPResponse: r}}
Ash Wilson01626a32014-09-17 10:38:07 -040048 p.MarkerPageBase.Owner = p
49 return p
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080050 }
51
Ash Wilsondc7daa82014-09-23 16:34:42 -040052 return pagination.NewPager(client, getDetailURL(client), createPage)
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080053}
54
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080055// Create requests a server to be provisioned to the user in the current tenant.
Ash Wilson01626a32014-09-17 10:38:07 -040056func Create(client *gophercloud.ServiceClient, opts map[string]interface{}) (ServerResult, error) {
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080057 var sr ServerResult
Ash Wilson01626a32014-09-17 10:38:07 -040058 _, err := perigee.Request("POST", getListURL(client), perigee.Options{
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080059 Results: &sr,
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080060 ReqBody: map[string]interface{}{
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080061 "server": opts,
62 },
Ash Wilson01626a32014-09-17 10:38:07 -040063 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080064 OkCodes: []int{202},
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080065 })
66 return sr, err
67}
68
69// Delete requests that a server previously provisioned be removed from your account.
Ash Wilson01626a32014-09-17 10:38:07 -040070func Delete(client *gophercloud.ServiceClient, id string) error {
71 _, err := perigee.Request("DELETE", getServerURL(client, id), perigee.Options{
72 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080073 OkCodes: []int{204},
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080074 })
75 return err
76}
77
Ash Wilson7ddf0362014-09-17 10:59:09 -040078// Get requests details on a single server, by ID.
79func Get(client *gophercloud.ServiceClient, id string) (ServerResult, error) {
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080080 var sr ServerResult
Ash Wilson01626a32014-09-17 10:38:07 -040081 _, err := perigee.Request("GET", getServerURL(client, id), perigee.Options{
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080082 Results: &sr,
Ash Wilson01626a32014-09-17 10:38:07 -040083 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080084 })
85 return sr, err
86}
87
Samuel A. Falvo II22d3b772014-02-13 19:27:05 -080088// Update requests that various attributes of the indicated server be changed.
Ash Wilson01626a32014-09-17 10:38:07 -040089func Update(client *gophercloud.ServiceClient, id string, opts map[string]interface{}) (ServerResult, error) {
Samuel A. Falvo II22d3b772014-02-13 19:27:05 -080090 var sr ServerResult
Ash Wilson01626a32014-09-17 10:38:07 -040091 _, err := perigee.Request("PUT", getServerURL(client, id), perigee.Options{
Samuel A. Falvo II22d3b772014-02-13 19:27:05 -080092 Results: &sr,
93 ReqBody: map[string]interface{}{
94 "server": opts,
95 },
Ash Wilson01626a32014-09-17 10:38:07 -040096 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Samuel A. Falvo II22d3b772014-02-13 19:27:05 -080097 })
98 return sr, err
99}
Samuel A. Falvo IIca5f9a32014-03-11 17:52:58 -0700100
Ash Wilson01626a32014-09-17 10:38:07 -0400101// ChangeAdminPassword alters the administrator or root password for a specified server.
102func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) error {
Ash Wilsondc7daa82014-09-23 16:34:42 -0400103 var req struct {
104 ChangePassword struct {
105 AdminPass string `json:"adminPass"`
106 } `json:"changePassword"`
107 }
108
109 req.ChangePassword.AdminPass = newPassword
110
Ash Wilson01626a32014-09-17 10:38:07 -0400111 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Ash Wilsondc7daa82014-09-23 16:34:42 -0400112 ReqBody: req,
Ash Wilson01626a32014-09-17 10:38:07 -0400113 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500114 OkCodes: []int{202},
Samuel A. Falvo IIca5f9a32014-03-11 17:52:58 -0700115 })
116 return err
117}
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700118
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700119// ErrArgument errors occur when an argument supplied to a package function
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700120// fails to fall within acceptable values. For example, the Reboot() function
121// expects the "how" parameter to be one of HardReboot or SoftReboot. These
122// constants are (currently) strings, leading someone to wonder if they can pass
123// other string values instead, perhaps in an effort to break the API of their
124// provider. Reboot() returns this error in this situation.
125//
126// Function identifies which function was called/which function is generating
127// the error.
128// Argument identifies which formal argument was responsible for producing the
129// error.
130// Value provides the value as it was passed into the function.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700131type ErrArgument struct {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700132 Function, Argument string
Jon Perritt30558642014-04-14 17:07:12 -0500133 Value interface{}
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700134}
135
136// Error yields a useful diagnostic for debugging purposes.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700137func (e *ErrArgument) Error() string {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700138 return fmt.Sprintf("Bad argument in call to %s, formal parameter %s, value %#v", e.Function, e.Argument, e.Value)
139}
140
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700141func (e *ErrArgument) String() string {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700142 return e.Error()
143}
144
Ash Wilson01626a32014-09-17 10:38:07 -0400145// RebootMethod describes the mechanisms by which a server reboot can be requested.
146type RebootMethod string
147
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700148// These constants determine how a server should be rebooted.
149// See the Reboot() function for further details.
150const (
Ash Wilson01626a32014-09-17 10:38:07 -0400151 SoftReboot RebootMethod = "SOFT"
152 HardReboot RebootMethod = "HARD"
153 OSReboot = SoftReboot
154 PowerCycle = HardReboot
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700155)
156
157// Reboot requests that a given server reboot.
158// Two methods exist for rebooting a server:
159//
Ash Wilson01626a32014-09-17 10:38:07 -0400160// HardReboot (aka PowerCycle) restarts the server instance by physically cutting power to the machine, or if a VM,
161// terminating it at the hypervisor level.
162// It's done. Caput. Full stop.
163// Then, after a brief while, power is restored or the VM instance restarted.
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700164//
Ash Wilson01626a32014-09-17 10:38:07 -0400165// SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures.
166// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine.
167func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) error {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700168 if (how != SoftReboot) && (how != HardReboot) {
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700169 return &ErrArgument{
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700170 Function: "Reboot",
171 Argument: "how",
Jon Perritt30558642014-04-14 17:07:12 -0500172 Value: how,
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700173 }
174 }
Jon Perritt30558642014-04-14 17:07:12 -0500175
Ash Wilson01626a32014-09-17 10:38:07 -0400176 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500177 ReqBody: struct {
178 C map[string]string `json:"reboot"`
179 }{
Ash Wilson01626a32014-09-17 10:38:07 -0400180 map[string]string{"type": string(how)},
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700181 },
Ash Wilson01626a32014-09-17 10:38:07 -0400182 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500183 OkCodes: []int{202},
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700184 })
185 return err
186}
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700187
Ash Wilson01626a32014-09-17 10:38:07 -0400188// Rebuild requests that the Openstack provider reprovision the server.
189// The rebuild will need to know the server's name and new image reference or ID.
190// In addition, and unlike building a server with Create(), you must provide an administrator password.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700191//
192// Additional options may be specified with the additional map.
193// This function treats a nil map the same as an empty map.
194//
Ash Wilson01626a32014-09-17 10:38:07 -0400195// Rebuild returns a server result as though you had called GetDetail() on the server's ID.
196// The information, however, refers to the new server, not the old.
197func Rebuild(client *gophercloud.ServiceClient, id, name, password, imageRef string, additional map[string]interface{}) (ServerResult, error) {
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700198 var sr ServerResult
199
200 if id == "" {
201 return sr, &ErrArgument{
202 Function: "Rebuild",
203 Argument: "id",
Jon Perritt30558642014-04-14 17:07:12 -0500204 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700205 }
206 }
207
208 if name == "" {
209 return sr, &ErrArgument{
210 Function: "Rebuild",
211 Argument: "name",
Jon Perritt30558642014-04-14 17:07:12 -0500212 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700213 }
214 }
215
216 if password == "" {
217 return sr, &ErrArgument{
218 Function: "Rebuild",
219 Argument: "password",
Jon Perritt30558642014-04-14 17:07:12 -0500220 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700221 }
222 }
223
224 if imageRef == "" {
225 return sr, &ErrArgument{
226 Function: "Rebuild",
227 Argument: "imageRef",
Jon Perritt30558642014-04-14 17:07:12 -0500228 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700229 }
230 }
231
232 if additional == nil {
233 additional = make(map[string]interface{}, 0)
234 }
235
236 additional["name"] = name
237 additional["imageRef"] = imageRef
238 additional["adminPass"] = password
239
Ash Wilson01626a32014-09-17 10:38:07 -0400240 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500241 ReqBody: struct {
242 R map[string]interface{} `json:"rebuild"`
243 }{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700244 additional,
245 },
Jon Perritt30558642014-04-14 17:07:12 -0500246 Results: &sr,
Ash Wilson01626a32014-09-17 10:38:07 -0400247 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500248 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700249 })
250 return sr, err
251}
252
253// Resize instructs the provider to change the flavor of the server.
Ash Wilson01626a32014-09-17 10:38:07 -0400254// Note that this implies rebuilding it.
255// Unfortunately, one cannot pass rebuild parameters to the resize function.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700256// When the resize completes, the server will be in RESIZE_VERIFY state.
257// While in this state, you can explore the use of the new server's configuration.
258// If you like it, call ConfirmResize() to commit the resize permanently.
259// Otherwise, call RevertResize() to restore the old configuration.
Ash Wilson01626a32014-09-17 10:38:07 -0400260func Resize(client *gophercloud.ServiceClient, id, flavorRef string) error {
261 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500262 ReqBody: struct {
263 R map[string]interface{} `json:"resize"`
264 }{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700265 map[string]interface{}{"flavorRef": flavorRef},
266 },
Ash Wilson01626a32014-09-17 10:38:07 -0400267 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500268 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700269 })
270 return err
271}
272
273// ConfirmResize confirms a previous resize operation on a server.
274// See Resize() for more details.
Ash Wilson01626a32014-09-17 10:38:07 -0400275func ConfirmResize(client *gophercloud.ServiceClient, id string) error {
276 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500277 ReqBody: map[string]interface{}{"confirmResize": nil},
Ash Wilson01626a32014-09-17 10:38:07 -0400278 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500279 OkCodes: []int{204},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700280 })
281 return err
282}
283
284// RevertResize cancels a previous resize operation on a server.
285// See Resize() for more details.
Ash Wilson01626a32014-09-17 10:38:07 -0400286func RevertResize(client *gophercloud.ServiceClient, id string) error {
287 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500288 ReqBody: map[string]interface{}{"revertResize": nil},
Ash Wilson01626a32014-09-17 10:38:07 -0400289 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500290 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700291 })
292 return err
293}