|  | package servers | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "github.com/racker/perigee" | 
|  | ) | 
|  |  | 
|  | // ListResult abstracts the raw results of making a List() request against the | 
|  | // API.  As OpenStack extensions may freely alter the response bodies of | 
|  | // structures returned to the client, you may only safely access the data | 
|  | // provided through separate, type-safe accessors or methods. | 
|  | type ListResult map[string]interface{} | 
|  |  | 
|  | // ServerResult abstracts a single server description, | 
|  | // as returned by the OpenStack provider. | 
|  | // As OpenStack extensions may freely alter the response bodies of the | 
|  | // structures returned to the client, | 
|  | // you may only safely access the data provided through | 
|  | // separate, type-safe accessors or methods. | 
|  | type ServerResult map[string]interface{} | 
|  |  | 
|  | // List makes a request against the API to list servers accessible to you. | 
|  | func List(c *Client) (ListResult, error) { | 
|  | var lr ListResult | 
|  |  | 
|  | h, err := c.getListHeaders() | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | err = perigee.Get(c.getListUrl(), perigee.Options{ | 
|  | Results:     &lr, | 
|  | MoreHeaders: h, | 
|  | }) | 
|  | return lr, err | 
|  | } | 
|  |  | 
|  | // Create requests a server to be provisioned to the user in the current tenant. | 
|  | func Create(c *Client, opts map[string]interface{}) (ServerResult, error) { | 
|  | var sr ServerResult | 
|  |  | 
|  | h, err := c.getCreateHeaders() | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | err = perigee.Post(c.getCreateUrl(), perigee.Options{ | 
|  | Results: &sr, | 
|  | ReqBody: map[string]interface{}{ | 
|  | "server": opts, | 
|  | }, | 
|  | MoreHeaders: h, | 
|  | OkCodes:     []int{202}, | 
|  | }) | 
|  | return sr, err | 
|  | } | 
|  |  | 
|  | // Delete requests that a server previously provisioned be removed from your account. | 
|  | func Delete(c *Client, id string) error { | 
|  | h, err := c.getDeleteHeaders() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = perigee.Delete(c.getDeleteUrl(id), perigee.Options{ | 
|  | MoreHeaders: h, | 
|  | OkCodes:     []int{204}, | 
|  | }) | 
|  | return err | 
|  | } | 
|  |  | 
|  | // GetDetail requests details on a single server, by ID. | 
|  | func GetDetail(c *Client, id string) (ServerResult, error) { | 
|  | var sr ServerResult | 
|  |  | 
|  | h, err := c.getDetailHeaders() | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | err = perigee.Get(c.getDetailUrl(id), perigee.Options{ | 
|  | Results:     &sr, | 
|  | MoreHeaders: h, | 
|  | }) | 
|  | return sr, err | 
|  | } | 
|  |  | 
|  | // Update requests that various attributes of the indicated server be changed. | 
|  | func Update(c *Client, id string, opts map[string]interface{}) (ServerResult, error) { | 
|  | var sr ServerResult | 
|  |  | 
|  | h, err := c.getUpdateHeaders() | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | err = perigee.Put(c.getUpdateUrl(id), perigee.Options{ | 
|  | Results: &sr, | 
|  | ReqBody: map[string]interface{}{ | 
|  | "server": opts, | 
|  | }, | 
|  | MoreHeaders: h, | 
|  | }) | 
|  | return sr, err | 
|  | } | 
|  |  | 
|  | // ChangeAdminPassword alters the administrator or root password for a specified | 
|  | // server. | 
|  | func ChangeAdminPassword(c *Client, id, newPassword string) error { | 
|  | h, err := c.getActionHeaders() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = perigee.Post(c.getActionUrl(id), perigee.Options{ | 
|  | ReqBody: struct { | 
|  | C map[string]string `json:"changePassword"` | 
|  | }{ | 
|  | map[string]string{"adminPass": newPassword}, | 
|  | }, | 
|  | MoreHeaders: h, | 
|  | OkCodes:     []int{202}, | 
|  | }) | 
|  | return err | 
|  | } | 
|  |  | 
|  | // ErrArgument errors occur when an argument supplied to a package function | 
|  | // fails to fall within acceptable values.  For example, the Reboot() function | 
|  | // expects the "how" parameter to be one of HardReboot or SoftReboot.  These | 
|  | // constants are (currently) strings, leading someone to wonder if they can pass | 
|  | // other string values instead, perhaps in an effort to break the API of their | 
|  | // provider.  Reboot() returns this error in this situation. | 
|  | // | 
|  | // Function identifies which function was called/which function is generating | 
|  | // the error. | 
|  | // Argument identifies which formal argument was responsible for producing the | 
|  | // error. | 
|  | // Value provides the value as it was passed into the function. | 
|  | type ErrArgument struct { | 
|  | Function, Argument string | 
|  | Value              interface{} | 
|  | } | 
|  |  | 
|  | // Error yields a useful diagnostic for debugging purposes. | 
|  | func (e *ErrArgument) Error() string { | 
|  | return fmt.Sprintf("Bad argument in call to %s, formal parameter %s, value %#v", e.Function, e.Argument, e.Value) | 
|  | } | 
|  |  | 
|  | func (e *ErrArgument) String() string { | 
|  | return e.Error() | 
|  | } | 
|  |  | 
|  | // These constants determine how a server should be rebooted. | 
|  | // See the Reboot() function for further details. | 
|  | const ( | 
|  | SoftReboot = "SOFT" | 
|  | HardReboot = "HARD" | 
|  | OSReboot   = SoftReboot | 
|  | PowerCycle = HardReboot | 
|  | ) | 
|  |  | 
|  | // Reboot requests that a given server reboot. | 
|  | // Two methods exist for rebooting a server: | 
|  | // | 
|  | // HardReboot (aka PowerCycle) -- restarts the server instance by physically | 
|  | // cutting power to the machine, or if a VM, terminating it at the hypervisor | 
|  | // level.  It's done.  Caput.  Full stop.  Then, after a brief while, power is | 
|  | // restored or the VM instance restarted. | 
|  | // | 
|  | // SoftReboot (aka OSReboot).  This approach simply tells the OS to restart | 
|  | // under its own procedures.  E.g., in Linux, asking it to enter runlevel 6, | 
|  | // or executing "sudo shutdown -r now", or by wasking Windows to restart the | 
|  | // machine. | 
|  | func Reboot(c *Client, id, how string) error { | 
|  | if (how != SoftReboot) && (how != HardReboot) { | 
|  | return &ErrArgument{ | 
|  | Function: "Reboot", | 
|  | Argument: "how", | 
|  | Value:    how, | 
|  | } | 
|  | } | 
|  |  | 
|  | h, err := c.getActionHeaders() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = perigee.Post(c.getActionUrl(id), perigee.Options{ | 
|  | ReqBody: struct { | 
|  | C map[string]string `json:"reboot"` | 
|  | }{ | 
|  | map[string]string{"type": how}, | 
|  | }, | 
|  | MoreHeaders: h, | 
|  | OkCodes:     []int{202}, | 
|  | }) | 
|  | return err | 
|  | } | 
|  |  | 
|  | // Rebuild requests that the Openstack provider reprovision the | 
|  | // server.  The rebuild will need to know the server's name and | 
|  | // new image reference or ID.  In addition, and unlike building | 
|  | // a server with Create(), you must provide an administrator | 
|  | // password. | 
|  | // | 
|  | // Additional options may be specified with the additional map. | 
|  | // This function treats a nil map the same as an empty map. | 
|  | // | 
|  | // Rebuild returns a server result as though you had called | 
|  | // GetDetail() on the server's ID.  The information, however, | 
|  | // refers to the new server, not the old. | 
|  | func Rebuild(c *Client, id, name, password, imageRef string, additional map[string]interface{}) (ServerResult, error) { | 
|  | var sr ServerResult | 
|  |  | 
|  | if id == "" { | 
|  | return sr, &ErrArgument{ | 
|  | Function: "Rebuild", | 
|  | Argument: "id", | 
|  | Value:    "", | 
|  | } | 
|  | } | 
|  |  | 
|  | if name == "" { | 
|  | return sr, &ErrArgument{ | 
|  | Function: "Rebuild", | 
|  | Argument: "name", | 
|  | Value:    "", | 
|  | } | 
|  | } | 
|  |  | 
|  | if password == "" { | 
|  | return sr, &ErrArgument{ | 
|  | Function: "Rebuild", | 
|  | Argument: "password", | 
|  | Value:    "", | 
|  | } | 
|  | } | 
|  |  | 
|  | if imageRef == "" { | 
|  | return sr, &ErrArgument{ | 
|  | Function: "Rebuild", | 
|  | Argument: "imageRef", | 
|  | Value:    "", | 
|  | } | 
|  | } | 
|  |  | 
|  | if additional == nil { | 
|  | additional = make(map[string]interface{}, 0) | 
|  | } | 
|  |  | 
|  | additional["name"] = name | 
|  | additional["imageRef"] = imageRef | 
|  | additional["adminPass"] = password | 
|  |  | 
|  | h, err := c.getActionHeaders() | 
|  | if err != nil { | 
|  | return sr, err | 
|  | } | 
|  |  | 
|  | err = perigee.Post(c.getActionUrl(id), perigee.Options{ | 
|  | ReqBody: struct { | 
|  | R map[string]interface{} `json:"rebuild"` | 
|  | }{ | 
|  | additional, | 
|  | }, | 
|  | Results:     &sr, | 
|  | MoreHeaders: h, | 
|  | OkCodes:     []int{202}, | 
|  | }) | 
|  | return sr, err | 
|  | } | 
|  |  | 
|  | // Resize instructs the provider to change the flavor of the server. | 
|  | // Note that this implies rebuilding it.  Unfortunately, one cannot pass rebuild parameters to the resize function. | 
|  | // When the resize completes, the server will be in RESIZE_VERIFY state. | 
|  | // While in this state, you can explore the use of the new server's configuration. | 
|  | // If you like it, call ConfirmResize() to commit the resize permanently. | 
|  | // Otherwise, call RevertResize() to restore the old configuration. | 
|  | func Resize(c *Client, id, flavorRef string) error { | 
|  | h, err := c.getActionHeaders() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = perigee.Post(c.getActionUrl(id), perigee.Options{ | 
|  | ReqBody: struct { | 
|  | R map[string]interface{} `json:"resize"` | 
|  | }{ | 
|  | map[string]interface{}{"flavorRef": flavorRef}, | 
|  | }, | 
|  | MoreHeaders: h, | 
|  | OkCodes:     []int{202}, | 
|  | }) | 
|  | return err | 
|  | } | 
|  |  | 
|  | // ConfirmResize confirms a previous resize operation on a server. | 
|  | // See Resize() for more details. | 
|  | func ConfirmResize(c *Client, id string) error { | 
|  | h, err := c.getActionHeaders() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = perigee.Post(c.getActionUrl(id), perigee.Options{ | 
|  | ReqBody:     map[string]interface{}{"confirmResize": nil}, | 
|  | MoreHeaders: h, | 
|  | OkCodes:     []int{204}, | 
|  | }) | 
|  | return err | 
|  | } | 
|  |  | 
|  | // RevertResize cancels a previous resize operation on a server. | 
|  | // See Resize() for more details. | 
|  | func RevertResize(c *Client, id string) error { | 
|  | h, err := c.getActionHeaders() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = perigee.Post(c.getActionUrl(id), perigee.Options{ | 
|  | ReqBody:     map[string]interface{}{"revertResize": nil}, | 
|  | MoreHeaders: h, | 
|  | OkCodes:     []int{202}, | 
|  | }) | 
|  | return err | 
|  | } |