blob: 95818daff3604467efad9931adfac2b636e3ccd8 [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"
Jon Perritt30558642014-04-14 17:07:12 -05005 "github.com/racker/perigee"
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -08006)
7
8// ListResult abstracts the raw results of making a List() request against the
9// API. As OpenStack extensions may freely alter the response bodies of
10// structures returned to the client, you may only safely access the data
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080011// provided through separate, type-safe accessors or methods.
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080012type ListResult map[string]interface{}
13
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -070014// ServerResult abstracts a single server description,
15// as returned by the OpenStack provider.
16// As OpenStack extensions may freely alter the response bodies of the
17// structures returned to the client,
18// you may only safely access the data provided through
19// separate, type-safe accessors or methods.
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080020type ServerResult map[string]interface{}
21
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080022// List makes a request against the API to list servers accessible to you.
23func List(c *Client) (ListResult, error) {
24 var lr ListResult
25
26 h, err := c.getListHeaders()
27 if err != nil {
28 return nil, err
29 }
30
31 err = perigee.Get(c.getListUrl(), perigee.Options{
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080032 Results: &lr,
Samuel A. Falvo IIc007c272014-02-10 20:49:26 -080033 MoreHeaders: h,
34 })
35 return lr, err
36}
37
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080038// Create requests a server to be provisioned to the user in the current tenant.
39func Create(c *Client, opts map[string]interface{}) (ServerResult, error) {
40 var sr ServerResult
41
42 h, err := c.getCreateHeaders()
43 if err != nil {
44 return nil, err
45 }
46
47 err = perigee.Post(c.getCreateUrl(), perigee.Options{
48 Results: &sr,
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080049 ReqBody: map[string]interface{}{
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080050 "server": opts,
51 },
52 MoreHeaders: h,
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080053 OkCodes: []int{202},
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080054 })
55 return sr, err
56}
57
58// Delete requests that a server previously provisioned be removed from your account.
59func Delete(c *Client, id string) error {
60 h, err := c.getDeleteHeaders()
61 if err != nil {
62 return err
63 }
64
65 err = perigee.Delete(c.getDeleteUrl(id), perigee.Options{
66 MoreHeaders: h,
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080067 OkCodes: []int{204},
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080068 })
69 return err
70}
71
72// GetDetail requests details on a single server, by ID.
73func GetDetail(c *Client, id string) (ServerResult, error) {
74 var sr ServerResult
75
76 h, err := c.getDetailHeaders()
77 if err != nil {
78 return nil, err
79 }
80
81 err = perigee.Get(c.getDetailUrl(id), perigee.Options{
Samuel A. Falvo IIe246ac02014-02-13 23:20:09 -080082 Results: &sr,
Samuel A. Falvo IIce000732014-02-13 18:53:53 -080083 MoreHeaders: h,
84 })
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.
89func Update(c *Client, id string, opts map[string]interface{}) (ServerResult, error) {
90 var sr ServerResult
91
92 h, err := c.getUpdateHeaders()
93 if err != nil {
94 return nil, err
95 }
96
97 err = perigee.Put(c.getUpdateUrl(id), perigee.Options{
98 Results: &sr,
99 ReqBody: map[string]interface{}{
100 "server": opts,
101 },
102 MoreHeaders: h,
103 })
104 return sr, err
105}
Samuel A. Falvo IIca5f9a32014-03-11 17:52:58 -0700106
107// ChangeAdminPassword alters the administrator or root password for a specified
108// server.
109func ChangeAdminPassword(c *Client, id, newPassword string) error {
110 h, err := c.getActionHeaders()
111 if err != nil {
112 return err
113 }
114
115 err = perigee.Post(c.getActionUrl(id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500116 ReqBody: struct {
117 C map[string]string `json:"changePassword"`
118 }{
Samuel A. Falvo IIca5f9a32014-03-11 17:52:58 -0700119 map[string]string{"adminPass": newPassword},
120 },
121 MoreHeaders: h,
Jon Perritt30558642014-04-14 17:07:12 -0500122 OkCodes: []int{202},
Samuel A. Falvo IIca5f9a32014-03-11 17:52:58 -0700123 })
124 return err
125}
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700126
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700127// ErrArgument errors occur when an argument supplied to a package function
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700128// fails to fall within acceptable values. For example, the Reboot() function
129// expects the "how" parameter to be one of HardReboot or SoftReboot. These
130// constants are (currently) strings, leading someone to wonder if they can pass
131// other string values instead, perhaps in an effort to break the API of their
132// provider. Reboot() returns this error in this situation.
133//
134// Function identifies which function was called/which function is generating
135// the error.
136// Argument identifies which formal argument was responsible for producing the
137// error.
138// Value provides the value as it was passed into the function.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700139type ErrArgument struct {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700140 Function, Argument string
Jon Perritt30558642014-04-14 17:07:12 -0500141 Value interface{}
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700142}
143
144// Error yields a useful diagnostic for debugging purposes.
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700145func (e *ErrArgument) Error() string {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700146 return fmt.Sprintf("Bad argument in call to %s, formal parameter %s, value %#v", e.Function, e.Argument, e.Value)
147}
148
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700149func (e *ErrArgument) String() string {
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700150 return e.Error()
151}
152
153// These constants determine how a server should be rebooted.
154// See the Reboot() function for further details.
155const (
156 SoftReboot = "SOFT"
157 HardReboot = "HARD"
Jon Perritt30558642014-04-14 17:07:12 -0500158 OSReboot = SoftReboot
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700159 PowerCycle = HardReboot
160)
161
162// Reboot requests that a given server reboot.
163// Two methods exist for rebooting a server:
164//
165// HardReboot (aka PowerCycle) -- restarts the server instance by physically
166// cutting power to the machine, or if a VM, terminating it at the hypervisor
167// level. It's done. Caput. Full stop. Then, after a brief while, power is
168// restored or the VM instance restarted.
169//
170// SoftReboot (aka OSReboot). This approach simply tells the OS to restart
171// under its own procedures. E.g., in Linux, asking it to enter runlevel 6,
172// or executing "sudo shutdown -r now", or by wasking Windows to restart the
173// machine.
174func Reboot(c *Client, id, how string) error {
175 if (how != SoftReboot) && (how != HardReboot) {
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700176 return &ErrArgument{
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700177 Function: "Reboot",
178 Argument: "how",
Jon Perritt30558642014-04-14 17:07:12 -0500179 Value: how,
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700180 }
181 }
Jon Perritt30558642014-04-14 17:07:12 -0500182
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700183 h, err := c.getActionHeaders()
184 if err != nil {
185 return err
186 }
187
188 err = perigee.Post(c.getActionUrl(id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500189 ReqBody: struct {
190 C map[string]string `json:"reboot"`
191 }{
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700192 map[string]string{"type": how},
193 },
194 MoreHeaders: h,
Jon Perritt30558642014-04-14 17:07:12 -0500195 OkCodes: []int{202},
Samuel A. Falvo II41c9f612014-03-11 19:00:10 -0700196 })
197 return err
198}
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700199
200// Rebuild requests that the Openstack provider reprovision the
201// server. The rebuild will need to know the server's name and
202// new image reference or ID. In addition, and unlike building
203// a server with Create(), you must provide an administrator
204// password.
205//
206// Additional options may be specified with the additional map.
207// This function treats a nil map the same as an empty map.
208//
209// Rebuild returns a server result as though you had called
210// GetDetail() on the server's ID. The information, however,
211// refers to the new server, not the old.
212func Rebuild(c *Client, id, name, password, imageRef string, additional map[string]interface{}) (ServerResult, error) {
213 var sr ServerResult
214
215 if id == "" {
216 return sr, &ErrArgument{
217 Function: "Rebuild",
218 Argument: "id",
Jon Perritt30558642014-04-14 17:07:12 -0500219 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700220 }
221 }
222
223 if name == "" {
224 return sr, &ErrArgument{
225 Function: "Rebuild",
226 Argument: "name",
Jon Perritt30558642014-04-14 17:07:12 -0500227 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700228 }
229 }
230
231 if password == "" {
232 return sr, &ErrArgument{
233 Function: "Rebuild",
234 Argument: "password",
Jon Perritt30558642014-04-14 17:07:12 -0500235 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700236 }
237 }
238
239 if imageRef == "" {
240 return sr, &ErrArgument{
241 Function: "Rebuild",
242 Argument: "imageRef",
Jon Perritt30558642014-04-14 17:07:12 -0500243 Value: "",
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700244 }
245 }
246
247 if additional == nil {
248 additional = make(map[string]interface{}, 0)
249 }
250
251 additional["name"] = name
252 additional["imageRef"] = imageRef
253 additional["adminPass"] = password
254
255 h, err := c.getActionHeaders()
256 if err != nil {
257 return sr, err
258 }
259
260 err = perigee.Post(c.getActionUrl(id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500261 ReqBody: struct {
262 R map[string]interface{} `json:"rebuild"`
263 }{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700264 additional,
265 },
Jon Perritt30558642014-04-14 17:07:12 -0500266 Results: &sr,
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700267 MoreHeaders: h,
Jon Perritt30558642014-04-14 17:07:12 -0500268 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700269 })
270 return sr, err
271}
272
273// Resize instructs the provider to change the flavor of the server.
274// Note that this implies rebuilding it. Unfortunately, one cannot pass rebuild parameters to the resize function.
275// When the resize completes, the server will be in RESIZE_VERIFY state.
276// While in this state, you can explore the use of the new server's configuration.
277// If you like it, call ConfirmResize() to commit the resize permanently.
278// Otherwise, call RevertResize() to restore the old configuration.
279func Resize(c *Client, id, flavorRef string) error {
280 h, err := c.getActionHeaders()
281 if err != nil {
282 return err
283 }
284
285 err = perigee.Post(c.getActionUrl(id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500286 ReqBody: struct {
287 R map[string]interface{} `json:"resize"`
288 }{
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700289 map[string]interface{}{"flavorRef": flavorRef},
290 },
291 MoreHeaders: h,
Jon Perritt30558642014-04-14 17:07:12 -0500292 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700293 })
294 return err
295}
296
297// ConfirmResize confirms a previous resize operation on a server.
298// See Resize() for more details.
299func ConfirmResize(c *Client, id string) error {
300 h, err := c.getActionHeaders()
301 if err != nil {
302 return err
303 }
304
305 err = perigee.Post(c.getActionUrl(id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500306 ReqBody: map[string]interface{}{"confirmResize": nil},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700307 MoreHeaders: h,
Jon Perritt30558642014-04-14 17:07:12 -0500308 OkCodes: []int{204},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700309 })
310 return err
311}
312
313// RevertResize cancels a previous resize operation on a server.
314// See Resize() for more details.
315func RevertResize(c *Client, id string) error {
316 h, err := c.getActionHeaders()
317 if err != nil {
318 return err
319 }
320
321 err = perigee.Post(c.getActionUrl(id), perigee.Options{
Jon Perritt30558642014-04-14 17:07:12 -0500322 ReqBody: map[string]interface{}{"revertResize": nil},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700323 MoreHeaders: h,
Jon Perritt30558642014-04-14 17:07:12 -0500324 OkCodes: []int{202},
Samuel A. Falvo II808bb632014-03-12 00:07:50 -0700325 })
326 return err
327}