Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logout and Chassis #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ aoscxgo

aoscxgo is a golang package that allows users to connect to and configure AOS-CX switches using REST API. The minimum supported firmware version is 10.09.

This package is forked from [Arubas own aoscxgo](https://github.com/aruba/aoscxgo) with some improvements.

Using aoscxgo
===========

Expand Down Expand Up @@ -37,7 +39,9 @@ func main() {

```

This will login to the switch and create a cookie to use for authentication in further calls. This cookie is stored within the aoscxgo.Client object that will be passed into configuration modules like so:
This will login to the switch and create a cookie to use for authentication in further calls. This cookie is stored within the aoscxgo.

### Work with VLAN's

```go
vlan100 := aoscxgo.Vlan{
Expand All @@ -59,10 +63,11 @@ This will login to the switch and create a cookie to use for authentication in f
log.Printf("VLAN Create Success")
```

Each API resource will have the following functions (exceptions may vary):
### Get some switch information

* `Create()`
* `Update()`
* `Get()`
* `GetStatus()`
* `Delete()`
```go
result, err := sw.GetChassis(1)
if err == nil {
log.Printf("Model %s, serial %s\n", result.ProductInfo.ProductName, result.ProductInfo.SerialNumber)
}
```
111 changes: 111 additions & 0 deletions chassis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package aoscxgo

import (
"fmt"
)

// Chassis represents the result from the chassis subsystem on v10.09.
type Chassis struct {
AclInitStatus string `json:"acl_init_status"`
AdminState string `json:"admin_state"`
AsicInfo struct {
} `json:"asic_info"`
Buttons string `json:"buttons"`
Capabilities []string `json:"capabilities"`
Capacities struct {
PsuSlots int `json:"psu_slots"`
} `json:"capacities"`
Daemons string `json:"daemons"`
DataPlaneConnectivityState struct {
} `json:"data_plane_connectivity_state"`
DataPlaneConnectivityTargetState struct {
} `json:"data_plane_connectivity_target_state"`
DataPlaneError struct {
} `json:"data_plane_error"`
DataPlaneState interface{} `json:"data_plane_state"`
DataPlaneTargetState interface{} `json:"data_plane_target_state"`
DataPlanes string `json:"data_planes"`
DiagTestResults string `json:"diag_test_results"`
DiagnosticDisable bool `json:"diagnostic_disable"`
DiagnosticLastRunTimestamp int `json:"diagnostic_last_run_timestamp"`
DiagnosticPerformed int `json:"diagnostic_performed"`
DiagnosticRequested int `json:"diagnostic_requested"`
EntityState struct {
} `json:"entity_state"`
FanConfigurationState interface{} `json:"fan_configuration_state"`
Fans string `json:"fans"`
Interfaces struct {
} `json:"interfaces"`
Leds string `json:"leds"`
MacsRemaining int `json:"macs_remaining"`
Name string `json:"name"`
NextMacAddress string `json:"next_mac_address"`
PacGbpInitStatus string `json:"pac_gbp_init_status"`
PartNumberCfg interface{} `json:"part_number_cfg"`
PoePower struct {
AvailablePower int `json:"available_power"`
FailoverPower int `json:"failover_power"`
PowerStatusRefreshTimestamp int `json:"power_status_refresh_timestamp"`
PowerStatusUpdateTimestamp int `json:"power_status_update_timestamp"`
RedundantPower int `json:"redundant_power"`
} `json:"poe_power"`
PolicyInitStatus string `json:"policy_init_status"`
PowerConsumed interface{} `json:"power_consumed"`
PowerSupplies string `json:"power_supplies"`
ProductInfo struct {
BaseMacAddress string `json:"base_mac_address"`
DeviceVersion string `json:"device_version"`
Instance string `json:"instance"`
NumberOfMacs string `json:"number_of_macs"`
PartNumber string `json:"part_number"`
ProductDescription string `json:"product_description"`
ProductName string `json:"product_name"`
SerialNumber string `json:"serial_number"`
Vendor string `json:"vendor"`
} `json:"product_info"`
PsuRedundancyOper string `json:"psu_redundancy_oper"`
PsuRedundancySet string `json:"psu_redundancy_set"`
RebootStatistics struct {
Configuration int `json:"configuration"`
Error int `json:"error"`
Hotswap int `json:"hotswap"`
Isp int `json:"isp"`
Thermal int `json:"thermal"`
User int `json:"user"`
} `json:"reboot_statistics"`
ResetsPerformed struct {
} `json:"resets_performed"`
ResetsRequested struct {
} `json:"resets_requested"`
ResourceCapacity struct {
} `json:"resource_capacity"`
ResourceReservationPerFeature struct {
} `json:"resource_reservation_per_feature"`
ResourceUnreserved struct {
} `json:"resource_unreserved"`
ResourceUtilization struct {
} `json:"resource_utilization"`
ResourceUtilizationPerFeature struct {
} `json:"resource_utilization_per_feature"`
ResourceWidthPerFeature struct {
} `json:"resource_width_per_feature"`
Selftest struct {
Status string `json:"status"`
} `json:"selftest"`
SelftestDisable bool `json:"selftest_disable"`
SoftwareImages struct {
} `json:"software_images"`
State string `json:"state"`
Storage struct {
} `json:"storage"`
TempSensors string `json:"temp_sensors"`
ThermalState string `json:"thermal_state"`
Type string `json:"type"`
UsbStatus string `json:"usb_status"`
}

// GetChassis returns the chassis information by the given id. The first chassis has id 1.
func (c *Client) GetChassis(id int) (Chassis, error) {
url := fmt.Sprintf("https://%s/rest/%s/system/subsystems/chassis,%v", c.Hostname, c.Version, id)
return getStruct[Chassis](c.Transport, c.Cookie, url)
}
34 changes: 23 additions & 11 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aoscxgo

import (
"crypto/tls"
"errors"
"fmt"
"log"
"net/http"
Expand Down Expand Up @@ -50,36 +51,47 @@ func Connect(c *Client) (*Client, error) {
// login performs POST to create a cookie for authentication to the given IP with the provided credentials.
func login(http_transport *http.Transport, ip string, rest_version string, username string, password string) (*http.Cookie, error) {
url := fmt.Sprintf("https://%s/rest/%s/login?username=%s&password=%s", ip, rest_version, username, password)
req, _ := http.NewRequest("POST", url, nil)
req, _ := http.NewRequest(http.MethodPost, url, nil)
req.Header.Set("accept", "*/*")
req.Close = false

res, err := http_transport.RoundTrip(req)
if res.Status != "200 OK" {
log.Fatalf("Got error while connecting to switch %s Error %s", res.Status, err)
if err != nil {
return nil, err
}
if res.StatusCode != http.StatusOK {
err = fmt.Errorf("Got error while connecting to switch %s Error %s\n", res.Status, err)
return nil, err
}

fmt.Println("Login Successful")
cookie := res.Cookies()[0]

return cookie, err
}

// logout performs POST to logout using a cookie from the given URL.
func logout(http_transport *http.Transport, cookie *http.Cookie, url string) *http.Response {
req, _ := http.NewRequest("POST", url, nil)
req, _ := http.NewRequest(http.MethodPost, url, nil)
req.Header.Set("accept", "*/*")
req.Close = false

req.AddCookie(cookie)
res, err := http_transport.RoundTrip(req)
//Handle Error
if res.Status != "200 OK" {
log.Fatalf("Got error while logging out of switch %s Error %s", res.Status, err)
if res.StatusCode != http.StatusOK {
log.Printf("Got error while logging out of switch %s Error %s\n", res.Status, err)
}

fmt.Println("Logout Successful")

return res
}

// Logout calls the logout endpoint to clear the session.
func (c *Client) Logout() error {
if c == nil {
return errors.New("nil value to Logout")
}
url := fmt.Sprintf("https://%s/rest/%s/logout", c.Hostname, c.Version)
resp := logout(c.Transport, c.Cookie, url)
if resp.StatusCode != http.StatusOK {
return errors.New(resp.Status)
}
return nil
}
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/aruba/aoscxgo
module github.com/helgeolav/aoscxgo

go 1.17
go 1.20

require bitbucket.org/HelgeOlav/utils v0.0.0-20230721223258-b289d4a9629b // indirect
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bitbucket.org/HelgeOlav/utils v0.0.0-20230512074710-6c72620c417f h1:n7uP8/UE3apY+Dx1wc5iKU3Wr/2dTGTNtaqBBYxpGBw=
bitbucket.org/HelgeOlav/utils v0.0.0-20230512074710-6c72620c417f/go.mod h1:jkRBnMNfcvCFkLAzx73xCu6VCnod3PuMpK9g1MUVfhA=
bitbucket.org/HelgeOlav/utils v0.0.0-20230721223258-b289d4a9629b h1:WhnQT47HDW1V9T5ic7g8D0dbbuoE59OVJWg2clyUKHs=
bitbucket.org/HelgeOlav/utils v0.0.0-20230721223258-b289d4a9629b/go.mod h1:jkRBnMNfcvCFkLAzx73xCu6VCnod3PuMpK9g1MUVfhA=
7 changes: 4 additions & 3 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"errors"
"net/http"
"net/url"
"regexp"
)
Expand Down Expand Up @@ -81,7 +82,7 @@ func (i *Interface) Create(c *Client) error {

res := post(c.Transport, c.Cookie, url, json_body)

if res.Status != "201 Created" {
if res.StatusCode != http.StatusCreated {
return &RequestError{
StatusCode: res.Status,
Err: errors.New("Create Error"),
Expand Down Expand Up @@ -126,7 +127,7 @@ func (i *Interface) Update(c *Client) error {

res := patch(c.Transport, c.Cookie, url, json_body)

if res.Status != "204 No Content" {
if res.StatusCode != http.StatusNoContent {
return &RequestError{
StatusCode: res.Status,
Err: errors.New("Update Error"),
Expand Down Expand Up @@ -173,7 +174,7 @@ func (i *Interface) Get(c *Client) error {

res, body := get(c.Transport, c.Cookie, url)

if res.Status != "200 OK" {
if res.StatusCode != http.StatusOK {
i.materialized = false
return &RequestError{
StatusCode: res.Status,
Expand Down
37 changes: 29 additions & 8 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,27 @@ package aoscxgo
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
)

// A custom error struct
// RequestError is a custom error struct
type RequestError struct {
StatusCode string

Err error
}

// A custom error string
// Error returns a custom error string
func (r *RequestError) Error() string {
return fmt.Sprintf("status %d: err %v", r.StatusCode, r.Err)
return fmt.Sprintf("status %s: err %v", r.StatusCode, r.Err)
}

// delete performs DELETE to the given URL and returns the response.
func delete(http_transport *http.Transport, cookie *http.Cookie, url string) *http.Response {
req, _ := http.NewRequest("DELETE", url, nil)
req, _ := http.NewRequest(http.MethodDelete, url, nil)
req.Header.Set("accept", "*/*")
req.Close = false

Expand All @@ -37,8 +38,9 @@ func delete(http_transport *http.Transport, cookie *http.Cookie, url string) *ht
}

// get performs GET to the given URL and returns the data response.
// Deprecated: use getStruct instead.
func get(http_transport *http.Transport, cookie *http.Cookie, url string) (*http.Response, map[string]interface{}) {
req, _ := http.NewRequest("GET", url, nil)
req, _ := http.NewRequest(http.MethodGet, url, nil)
req.Header.Set("accept", "*/*")
req.Close = false
req.AddCookie(cookie)
Expand All @@ -53,18 +55,37 @@ func get(http_transport *http.Transport, cookie *http.Cookie, url string) (*http
return res, body
}

// getStruct performs GET to the given URL and returns the response as struct.
func getStruct[T any](http_transport *http.Transport, cookie *http.Cookie, url string) (T, error) {
var result T
req, _ := http.NewRequest(http.MethodGet, url, nil)
req.Header.Set("accept", "*/*")
req.Close = false
req.AddCookie(cookie)
res, err := http_transport.RoundTrip(req)
if err != nil {
return result, err
}
if res.StatusCode == http.StatusOK {
err = json.NewDecoder(res.Body).Decode(&result)
} else {
err = errors.New(res.Status)
}

return result, err
}

// post performs POST to the given URL and returns the response.
func post(http_transport *http.Transport, cookie *http.Cookie, url string, json_body *bytes.Buffer) *http.Response {
req, _ := http.NewRequest("POST", url, json_body)
req, _ := http.NewRequest(http.MethodPost, url, json_body)
req.Header.Set("accept", "*/*")
req.Close = false

req.AddCookie(cookie)
res, err := http_transport.RoundTrip(req)
//Handle Error
if err != nil {
log.Fatalf("An Error Occured %v %v", err, res.Status)
//os.Exit(1)
log.Printf("An Error Occured %s %s\n", err, res.Status)
}

return res
Expand Down
12 changes: 12 additions & 0 deletions version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package aoscxgo

import "bitbucket.org/HelgeOlav/utils/version"

const Version = "0.0.2"

func init() {
version.AddModule(version.ModuleVersion{
Name: "aoscxgo",
Version: Version,
})
}