blob: 9e6581790ba4a28c30fdd6789d4e886bb48e9b97 [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 Wilson01626a32014-09-17 10:38:07 -040052 return pagination.NewPager(client, getListURL(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 {
103 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500104 ReqBody: struct {
105 C map[string]string `json:"changePassword"`
106 }{
Samuel A. Falvo IIca5f9a32014-03-11 17:52:58 -0700107 map[string]string{"adminPass": newPassword},
108 },
Ash Wilson01626a32014-09-17 10:38:07 -0400109 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500110 OkCodes: []int{202},
Samuel A. Falvo IIca5f9a32014-03-11 17:52:58 -0700111 })
112 return err
113}
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700114
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700115// ErrArgument errors occur when an argument supplied to a package function
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700116// fails to fall within acceptable values. For example, the Reboot() function
117// expects the "how" parameter to be one of HardReboot or SoftReboot. These
118// constants are (currently) strings, leading someone to wonder if they can pass
119// other string values instead, perhaps in an effort to break the API of their
120// provider. Reboot() returns this error in this situation.
121//
122// Function identifies which function was called/which function is generating
123// the error.
124// Argument identifies which formal argument was responsible for producing the
125// error.
126// Value provides the value as it was passed into the function.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700127type ErrArgument struct {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700128 Function, Argument string
Jon Perritt30558642014-04-14 17:07:12 -0500129 Value interface{}
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700130}
131
132// Error yields a useful diagnostic for debugging purposes.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700133func (e *ErrArgument) Error() string {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700134 return fmt.Sprintf("Bad argument in call to %s, formal parameter %s, value %#v", e.Function, e.Argument, e.Value)
135}
136
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700137func (e *ErrArgument) String() string {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700138 return e.Error()
139}
140
Ash Wilson01626a32014-09-17 10:38:07 -0400141// RebootMethod describes the mechanisms by which a server reboot can be requested.
142type RebootMethod string
143
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700144// These constants determine how a server should be rebooted.
145// See the Reboot() function for further details.
146const (
Ash Wilson01626a32014-09-17 10:38:07 -0400147 SoftReboot RebootMethod = "SOFT"
148 HardReboot RebootMethod = "HARD"
149 OSReboot = SoftReboot
150 PowerCycle = HardReboot
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700151)
152
153// Reboot requests that a given server reboot.
154// Two methods exist for rebooting a server:
155//
Ash Wilson01626a32014-09-17 10:38:07 -0400156// HardReboot (aka PowerCycle) restarts the server instance by physically cutting power to the machine, or if a VM,
157// terminating it at the hypervisor level.
158// It's done. Caput. Full stop.
159// Then, after a brief while, power is restored or the VM instance restarted.
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700160//
Ash Wilson01626a32014-09-17 10:38:07 -0400161// SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures.
162// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine.
163func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) error {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700164 if (how != SoftReboot) && (how != HardReboot) {
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700165 return &ErrArgument{
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700166 Function: "Reboot",
167 Argument: "how",
Jon Perritt30558642014-04-14 17:07:12 -0500168 Value: how,
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700169 }
170 }
Jon Perritt30558642014-04-14 17:07:12 -0500171
Ash Wilson01626a32014-09-17 10:38:07 -0400172 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500173 ReqBody: struct {
174 C map[string]string `json:"reboot"`
175 }{
Ash Wilson01626a32014-09-17 10:38:07 -0400176 map[string]string{"type": string(how)},
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700177 },
Ash Wilson01626a32014-09-17 10:38:07 -0400178 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500179 OkCodes: []int{202},
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700180 })
181 return err
182}
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700183
Ash Wilson01626a32014-09-17 10:38:07 -0400184// Rebuild requests that the Openstack provider reprovision the server.
185// The rebuild will need to know the server's name and new image reference or ID.
186// In addition, and unlike building a server with Create(), you must provide an administrator password.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700187//
188// Additional options may be specified with the additional map.
189// This function treats a nil map the same as an empty map.
190//
Ash Wilson01626a32014-09-17 10:38:07 -0400191// Rebuild returns a server result as though you had called GetDetail() on the server's ID.
192// The information, however, refers to the new server, not the old.
193func Rebuild(client *gophercloud.ServiceClient, id, name, password, imageRef string, additional map[string]interface{}) (ServerResult, error) {
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700194 var sr ServerResult
195
196 if id == "" {
197 return sr, &ErrArgument{
198 Function: "Rebuild",
199 Argument: "id",
Jon Perritt30558642014-04-14 17:07:12 -0500200 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700201 }
202 }
203
204 if name == "" {
205 return sr, &ErrArgument{
206 Function: "Rebuild",
207 Argument: "name",
Jon Perritt30558642014-04-14 17:07:12 -0500208 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700209 }
210 }
211
212 if password == "" {
213 return sr, &ErrArgument{
214 Function: "Rebuild",
215 Argument: "password",
Jon Perritt30558642014-04-14 17:07:12 -0500216 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700217 }
218 }
219
220 if imageRef == "" {
221 return sr, &ErrArgument{
222 Function: "Rebuild",
223 Argument: "imageRef",
Jon Perritt30558642014-04-14 17:07:12 -0500224 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700225 }
226 }
227
228 if additional == nil {
229 additional = make(map[string]interface{}, 0)
230 }
231
232 additional["name"] = name
233 additional["imageRef"] = imageRef
234 additional["adminPass"] = password
235
Ash Wilson01626a32014-09-17 10:38:07 -0400236 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500237 ReqBody: struct {
238 R map[string]interface{} `json:"rebuild"`
239 }{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700240 additional,
241 },
Jon Perritt30558642014-04-14 17:07:12 -0500242 Results: &sr,
Ash Wilson01626a32014-09-17 10:38:07 -0400243 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500244 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700245 })
246 return sr, err
247}
248
249// Resize instructs the provider to change the flavor of the server.
Ash Wilson01626a32014-09-17 10:38:07 -0400250// Note that this implies rebuilding it.
251// Unfortunately, one cannot pass rebuild parameters to the resize function.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700252// When the resize completes, the server will be in RESIZE_VERIFY state.
253// While in this state, you can explore the use of the new server's configuration.
254// If you like it, call ConfirmResize() to commit the resize permanently.
255// Otherwise, call RevertResize() to restore the old configuration.
Ash Wilson01626a32014-09-17 10:38:07 -0400256func Resize(client *gophercloud.ServiceClient, id, flavorRef string) error {
257 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500258 ReqBody: struct {
259 R map[string]interface{} `json:"resize"`
260 }{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700261 map[string]interface{}{"flavorRef": flavorRef},
262 },
Ash Wilson01626a32014-09-17 10:38:07 -0400263 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500264 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700265 })
266 return err
267}
268
269// ConfirmResize confirms a previous resize operation on a server.
270// See Resize() for more details.
Ash Wilson01626a32014-09-17 10:38:07 -0400271func ConfirmResize(client *gophercloud.ServiceClient, id string) error {
272 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500273 ReqBody: map[string]interface{}{"confirmResize": nil},
Ash Wilson01626a32014-09-17 10:38:07 -0400274 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500275 OkCodes: []int{204},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700276 })
277 return err
278}
279
280// RevertResize cancels a previous resize operation on a server.
281// See Resize() for more details.
Ash Wilson01626a32014-09-17 10:38:07 -0400282func RevertResize(client *gophercloud.ServiceClient, id string) error {
283 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500284 ReqBody: map[string]interface{}{"revertResize": nil},
Ash Wilson01626a32014-09-17 10:38:07 -0400285 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500286 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700287 })
288 return err
289}