blob: 3b23be5efec3b5a70634e413a33211b9ad25ae27 [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
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080039// List makes a request against the API to list servers accessible to you.
Ash Wilson01626a32014-09-17 10:38:07 -040040func List(client *gophercloud.ServiceClient) pagination.Pager {
41 createPage := func(r pagination.LastHTTPResponse) pagination.Page {
Ash Wilsonfd043792014-09-17 10:40:17 -040042 p := ListPage{pagination.MarkerPageBase{LastHTTPResponse: r}}
Ash Wilson01626a32014-09-17 10:38:07 -040043 p.MarkerPageBase.Owner = p
44 return p
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080045 }
46
Ash Wilsondc7daa82014-09-23 16:34:42 -040047 return pagination.NewPager(client, getDetailURL(client), createPage)
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080048}
49
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080050// Create requests a server to be provisioned to the user in the current tenant.
Ash Wilsond27e0ff2014-09-25 11:50:31 -040051func Create(client *gophercloud.ServiceClient, opts map[string]interface{}) CreateResult {
52 var result CreateResult
53 _, result.Err = perigee.Request("POST", getListURL(client), perigee.Options{
54 Results: &result.Resp,
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080055 ReqBody: map[string]interface{}{
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080056 "server": opts,
57 },
Ash Wilson01626a32014-09-17 10:38:07 -040058 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080059 OkCodes: []int{202},
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080060 })
Ash Wilsond27e0ff2014-09-25 11:50:31 -040061 return result
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080062}
63
64// Delete requests that a server previously provisioned be removed from your account.
Ash Wilson01626a32014-09-17 10:38:07 -040065func Delete(client *gophercloud.ServiceClient, id string) error {
66 _, err := perigee.Request("DELETE", getServerURL(client, id), perigee.Options{
67 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080068 OkCodes: []int{204},
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080069 })
70 return err
71}
72
Ash Wilson7ddf0362014-09-17 10:59:09 -040073// Get requests details on a single server, by ID.
Ash Wilsond27e0ff2014-09-25 11:50:31 -040074func Get(client *gophercloud.ServiceClient, id string) GetResult {
75 var result GetResult
76 _, result.Err = perigee.Request("GET", getServerURL(client, id), perigee.Options{
77 Results: &result.Resp,
Ash Wilson01626a32014-09-17 10:38:07 -040078 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080079 })
Ash Wilsond27e0ff2014-09-25 11:50:31 -040080 return result
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080081}
82
Samuel A. Falvo II22d3b772014-02-13 19:27:05 -080083// Update requests that various attributes of the indicated server be changed.
Ash Wilsond27e0ff2014-09-25 11:50:31 -040084func Update(client *gophercloud.ServiceClient, id string, opts map[string]interface{}) UpdateResult {
85 var result UpdateResult
86 _, result.Err = perigee.Request("PUT", getServerURL(client, id), perigee.Options{
87 Results: &result.Resp,
Samuel A. Falvo II22d3b772014-02-13 19:27:05 -080088 ReqBody: map[string]interface{}{
89 "server": opts,
90 },
Ash Wilson01626a32014-09-17 10:38:07 -040091 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Samuel A. Falvo II22d3b772014-02-13 19:27:05 -080092 })
Ash Wilsond27e0ff2014-09-25 11:50:31 -040093 return result
Samuel A. Falvo II22d3b772014-02-13 19:27:05 -080094}
Samuel A. Falvo IIca5f9a32014-03-11 17:52:58 -070095
Ash Wilson01626a32014-09-17 10:38:07 -040096// ChangeAdminPassword alters the administrator or root password for a specified server.
97func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) error {
Ash Wilsondc7daa82014-09-23 16:34:42 -040098 var req struct {
99 ChangePassword struct {
100 AdminPass string `json:"adminPass"`
101 } `json:"changePassword"`
102 }
103
104 req.ChangePassword.AdminPass = newPassword
105
Ash Wilson01626a32014-09-17 10:38:07 -0400106 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Ash Wilsondc7daa82014-09-23 16:34:42 -0400107 ReqBody: req,
Ash Wilson01626a32014-09-17 10:38:07 -0400108 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500109 OkCodes: []int{202},
Samuel A. Falvo IIca5f9a32014-03-11 17:52:58 -0700110 })
111 return err
112}
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700113
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700114// ErrArgument errors occur when an argument supplied to a package function
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700115// fails to fall within acceptable values. For example, the Reboot() function
116// expects the "how" parameter to be one of HardReboot or SoftReboot. These
117// constants are (currently) strings, leading someone to wonder if they can pass
118// other string values instead, perhaps in an effort to break the API of their
119// provider. Reboot() returns this error in this situation.
120//
121// Function identifies which function was called/which function is generating
122// the error.
123// Argument identifies which formal argument was responsible for producing the
124// error.
125// Value provides the value as it was passed into the function.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700126type ErrArgument struct {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700127 Function, Argument string
Jon Perritt30558642014-04-14 17:07:12 -0500128 Value interface{}
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700129}
130
131// Error yields a useful diagnostic for debugging purposes.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700132func (e *ErrArgument) Error() string {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700133 return fmt.Sprintf("Bad argument in call to %s, formal parameter %s, value %#v", e.Function, e.Argument, e.Value)
134}
135
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700136func (e *ErrArgument) String() string {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700137 return e.Error()
138}
139
Ash Wilson01626a32014-09-17 10:38:07 -0400140// RebootMethod describes the mechanisms by which a server reboot can be requested.
141type RebootMethod string
142
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700143// These constants determine how a server should be rebooted.
144// See the Reboot() function for further details.
145const (
Ash Wilson01626a32014-09-17 10:38:07 -0400146 SoftReboot RebootMethod = "SOFT"
147 HardReboot RebootMethod = "HARD"
148 OSReboot = SoftReboot
149 PowerCycle = HardReboot
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700150)
151
152// Reboot requests that a given server reboot.
153// Two methods exist for rebooting a server:
154//
Ash Wilson01626a32014-09-17 10:38:07 -0400155// HardReboot (aka PowerCycle) restarts the server instance by physically cutting power to the machine, or if a VM,
156// terminating it at the hypervisor level.
157// It's done. Caput. Full stop.
158// Then, after a brief while, power is restored or the VM instance restarted.
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700159//
Ash Wilson01626a32014-09-17 10:38:07 -0400160// SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures.
161// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine.
162func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) error {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700163 if (how != SoftReboot) && (how != HardReboot) {
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700164 return &ErrArgument{
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700165 Function: "Reboot",
166 Argument: "how",
Jon Perritt30558642014-04-14 17:07:12 -0500167 Value: how,
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700168 }
169 }
Jon Perritt30558642014-04-14 17:07:12 -0500170
Ash Wilson01626a32014-09-17 10:38:07 -0400171 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500172 ReqBody: struct {
173 C map[string]string `json:"reboot"`
174 }{
Ash Wilson01626a32014-09-17 10:38:07 -0400175 map[string]string{"type": string(how)},
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700176 },
Ash Wilson01626a32014-09-17 10:38:07 -0400177 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500178 OkCodes: []int{202},
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700179 })
180 return err
181}
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700182
Ash Wilson01626a32014-09-17 10:38:07 -0400183// Rebuild requests that the Openstack provider reprovision the server.
184// The rebuild will need to know the server's name and new image reference or ID.
185// In addition, and unlike building a server with Create(), you must provide an administrator password.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700186//
187// Additional options may be specified with the additional map.
188// This function treats a nil map the same as an empty map.
189//
Ash Wilson01626a32014-09-17 10:38:07 -0400190// Rebuild returns a server result as though you had called GetDetail() on the server's ID.
191// The information, however, refers to the new server, not the old.
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400192func Rebuild(client *gophercloud.ServiceClient, id, name, password, imageRef string, additional map[string]interface{}) RebuildResult {
193 var result RebuildResult
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700194
195 if id == "" {
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400196 result.Err = &ErrArgument{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700197 Function: "Rebuild",
198 Argument: "id",
Jon Perritt30558642014-04-14 17:07:12 -0500199 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700200 }
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400201 return result
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700202 }
203
204 if name == "" {
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400205 result.Err = &ErrArgument{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700206 Function: "Rebuild",
207 Argument: "name",
Jon Perritt30558642014-04-14 17:07:12 -0500208 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700209 }
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400210 return result
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700211 }
212
213 if password == "" {
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400214 result.Err = &ErrArgument{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700215 Function: "Rebuild",
216 Argument: "password",
Jon Perritt30558642014-04-14 17:07:12 -0500217 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700218 }
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400219 return result
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700220 }
221
222 if imageRef == "" {
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400223 result.Err = &ErrArgument{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700224 Function: "Rebuild",
225 Argument: "imageRef",
Jon Perritt30558642014-04-14 17:07:12 -0500226 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700227 }
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400228 return result
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700229 }
230
231 if additional == nil {
232 additional = make(map[string]interface{}, 0)
233 }
234
235 additional["name"] = name
236 additional["imageRef"] = imageRef
237 additional["adminPass"] = password
238
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400239 _, result.Err = perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500240 ReqBody: struct {
241 R map[string]interface{} `json:"rebuild"`
242 }{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700243 additional,
244 },
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400245 Results: &result.Resp,
Ash Wilson01626a32014-09-17 10:38:07 -0400246 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500247 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700248 })
Ash Wilsond27e0ff2014-09-25 11:50:31 -0400249 return result
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700250}
251
252// Resize instructs the provider to change the flavor of the server.
Ash Wilson01626a32014-09-17 10:38:07 -0400253// Note that this implies rebuilding it.
254// Unfortunately, one cannot pass rebuild parameters to the resize function.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700255// When the resize completes, the server will be in RESIZE_VERIFY state.
256// While in this state, you can explore the use of the new server's configuration.
257// If you like it, call ConfirmResize() to commit the resize permanently.
258// Otherwise, call RevertResize() to restore the old configuration.
Ash Wilson01626a32014-09-17 10:38:07 -0400259func Resize(client *gophercloud.ServiceClient, id, flavorRef string) error {
260 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500261 ReqBody: struct {
262 R map[string]interface{} `json:"resize"`
263 }{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700264 map[string]interface{}{"flavorRef": flavorRef},
265 },
Ash Wilson01626a32014-09-17 10:38:07 -0400266 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500267 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700268 })
269 return err
270}
271
272// ConfirmResize confirms a previous resize operation on a server.
273// See Resize() for more details.
Ash Wilson01626a32014-09-17 10:38:07 -0400274func ConfirmResize(client *gophercloud.ServiceClient, id string) error {
275 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500276 ReqBody: map[string]interface{}{"confirmResize": nil},
Ash Wilson01626a32014-09-17 10:38:07 -0400277 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500278 OkCodes: []int{204},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700279 })
280 return err
281}
282
283// RevertResize cancels a previous resize operation on a server.
284// See Resize() for more details.
Ash Wilson01626a32014-09-17 10:38:07 -0400285func RevertResize(client *gophercloud.ServiceClient, id string) error {
286 _, err := perigee.Request("POST", getActionURL(client, id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500287 ReqBody: map[string]interface{}{"revertResize": nil},
Ash Wilson01626a32014-09-17 10:38:07 -0400288 MoreHeaders: client.Provider.AuthenticatedHeaders(),
Jon Perritt30558642014-04-14 17:07:12 -0500289 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700290 })
291 return err
292}