| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package devices |
| |
| import ( |
| "bytes" |
| "crypto/tls" |
| "encoding/json" |
| "fmt" |
| "net/http" |
| "net/url" |
| ) |
| |
| // PDUConn encapsultes PDU connection settings. |
| type PDUConn struct { |
| // Client is the client used to perform all requests. |
| Client *http.Client |
| |
| // Host is the network hostname of the PDU. |
| Host string |
| |
| // Username is the username by which we can log in to the PDU. |
| Username string |
| |
| // Password is the password by which we can log in to the PDU. |
| Password string |
| |
| // token is the authentication token obtained by Login. |
| token string |
| } |
| |
| type message struct { |
| ID int `json:"msgid,omitempty"` |
| Reply string `json:"reply,omitempty"` |
| Data json.RawMessage `json:"data,omitempty"` |
| Error struct { |
| Name string `json:"name"` |
| Message string `json:"message"` |
| Status int `json:"status"` |
| } `json:"error,omitempty"` |
| } |
| |
| // NewPDUConn initializes and returns a new PDUConn struct. |
| func NewPDUConn(host string, uname string, pwd string) *PDUConn { |
| return &PDUConn{ |
| Client: &http.Client{ |
| Transport: &http.Transport{ |
| TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
| }, |
| }, |
| Host: host, |
| Username: uname, |
| Password: pwd, |
| } |
| } |
| |
| // Login performs a login and obtains authorization token used for other calls. |
| func (c *PDUConn) Login() error { |
| q := make(url.Values) |
| q.Set("username", c.Username) |
| q.Set("password", c.Password) |
| |
| u := url.URL{ |
| Scheme: "https", |
| Host: c.Host, |
| Path: "/api/AuthenticationControllers/login", |
| RawQuery: q.Encode(), |
| } |
| |
| l := struct { |
| Username string `json:"username"` |
| Password string `json:"password"` |
| }{ |
| Username: c.Username, |
| Password: c.Password, |
| } |
| |
| b := new(bytes.Buffer) |
| if err := json.NewEncoder(b).Encode(l); err != nil { |
| return err |
| } |
| |
| req, err := http.NewRequest("POST", u.String(), b) |
| req.Header.Add("Accept", "application/json") |
| req.Header.Set("Content-Type", "application/json") |
| if err != nil { |
| return err |
| } |
| resp, err := c.Client.Do(req) |
| if err != nil { |
| return err |
| } |
| defer resp.Body.Close() |
| |
| var msg message |
| err = json.NewDecoder(resp.Body).Decode(&msg) |
| |
| if resp.StatusCode != http.StatusCreated { |
| if err != nil { |
| return fmt.Errorf("request failed: %d", resp.StatusCode) |
| } else { |
| return fmt.Errorf("request failed: %s", msg.Error.Message) |
| } |
| } |
| |
| if token, ok := resp.Header["Authorization"]; ok { |
| c.token = token[0] |
| } |
| |
| return nil |
| } |
| |
| // PDUReboot powercycles the given outlet. |
| func PDUReboot(outlet int, host string, uname string, pwd string) error { |
| conn := NewPDUConn(host, uname, pwd) |
| if err := conn.Login(); err != nil { |
| return err |
| } |
| defer conn.Logout() |
| return conn.Loads(outlet, "Cycle") |
| } |
| |
| // Loads changes the outlet state. |
| func (c *PDUConn) Loads(outlet int, state string) error { |
| u := url.URL{ |
| Scheme: "https", |
| Host: c.Host, |
| Path: fmt.Sprintf("/api/device/loads/%d", outlet), |
| } |
| |
| s := struct { |
| LoadFireState string `json:"loadFireState"` |
| }{ |
| LoadFireState: state, |
| } |
| |
| b := new(bytes.Buffer) |
| if err := json.NewEncoder(b).Encode(s); err != nil { |
| return err |
| } |
| |
| req, err := http.NewRequest("PUT", u.String(), b) |
| if err != nil { |
| return err |
| } |
| req.Header.Add("Accept", "application/json") |
| req.Header.Add("Authorization", c.token) |
| req.Header.Set("Content-Type", "application/json") |
| resp, err := c.Client.Do(req) |
| if err != nil { |
| return err |
| } |
| defer resp.Body.Close() |
| |
| var msg message |
| err = json.NewDecoder(resp.Body).Decode(&msg) |
| |
| if resp.StatusCode != http.StatusOK { |
| if err != nil { |
| return fmt.Errorf("request failed: %d", resp.StatusCode) |
| } else { |
| return fmt.Errorf("request failed: %s", msg.Error.Message) |
| } |
| } |
| |
| return nil |
| } |
| |
| // Logout performs a logout and discards the authorization token. |
| func (c *PDUConn) Logout() error { |
| u := url.URL{ |
| Scheme: "https", |
| Host: c.Host, |
| Path: "/api/AuthenticationControllers/logout", |
| } |
| |
| req, err := http.NewRequest("POST", u.String(), nil) |
| req.Header.Add("Accept", "application/json") |
| req.Header.Add("Authorization", c.token) |
| req.Header.Set("Content-Type", "application/json") |
| if err != nil { |
| return err |
| } |
| resp, err := c.Client.Do(req) |
| if err != nil { |
| return err |
| } |
| defer resp.Body.Close() |
| |
| var msg message |
| err = json.NewDecoder(resp.Body).Decode(&msg) |
| |
| if resp.StatusCode != http.StatusOK { |
| if err != nil { |
| return fmt.Errorf("request failed: %d", resp.StatusCode) |
| } else { |
| return fmt.Errorf("request failed: %s", msg.Error.Message) |
| } |
| } |
| |
| c.token = "" |
| |
| return nil |
| } |