mirror of
https://github.com/davidallendj/magellan.git
synced 2025-12-20 03:27:03 -07:00
More minor changes
This commit is contained in:
parent
e02558fd00
commit
c0a6d3bb6f
5 changed files with 227 additions and 18 deletions
|
|
@ -9,8 +9,6 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/OpenCHAMI/magellan/internal/util"
|
||||
)
|
||||
|
||||
type Option[T Client] func(client T)
|
||||
|
|
@ -24,8 +22,8 @@ type Client interface {
|
|||
RootEndpoint(endpoint string) string
|
||||
|
||||
// functions needed to make request
|
||||
Add(data util.HTTPBody, headers util.HTTPHeader) error
|
||||
Update(data util.HTTPBody, headers util.HTTPHeader) error
|
||||
Add(data HTTPBody, headers HTTPHeader) error
|
||||
Update(data HTTPBody, headers HTTPHeader) error
|
||||
}
|
||||
|
||||
// NewClient() creates a new client
|
||||
|
|
@ -71,11 +69,11 @@ func WithSecureTLS[T Client](certPath string) func(T) {
|
|||
// Post() is a simplified wrapper function that packages all of the
|
||||
// that marshals a mapper into a JSON-formatted byte array, and then performs
|
||||
// a request to the specified URL.
|
||||
func (c *MagellanClient) Post(url string, data map[string]any, header util.HTTPHeader) (*http.Response, util.HTTPBody, error) {
|
||||
func (c *MagellanClient) Post(url string, data map[string]any, header HTTPHeader) (*http.Response, HTTPBody, error) {
|
||||
// serialize data into byte array
|
||||
body, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to marshal data for request: %v", err)
|
||||
}
|
||||
return util.MakeRequest(c.Client, url, http.MethodPost, body, header)
|
||||
return MakeRequest(c.Client, url, http.MethodPost, body, header)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package client
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/OpenCHAMI/magellan/internal/util"
|
||||
)
|
||||
|
||||
type MagellanClient struct {
|
||||
|
|
@ -20,13 +18,13 @@ func (c *MagellanClient) Name() string {
|
|||
// the first argument with no data processing or manipulation. The function sends
|
||||
// the data to a set callback URL (which may be changed to use a configurable value
|
||||
// instead).
|
||||
func (c *MagellanClient) Add(data util.HTTPBody, headers util.HTTPHeader) error {
|
||||
func (c *MagellanClient) Add(data HTTPBody, headers HTTPHeader) error {
|
||||
if data == nil {
|
||||
return fmt.Errorf("no data found")
|
||||
}
|
||||
|
||||
path := "/inventory/add"
|
||||
res, body, err := util.MakeRequest(c.Client, path, http.MethodPost, data, headers)
|
||||
res, body, err := MakeRequest(c.Client, path, http.MethodPost, data, headers)
|
||||
if res != nil {
|
||||
statusOk := res.StatusCode >= 200 && res.StatusCode < 300
|
||||
if !statusOk {
|
||||
|
|
@ -37,13 +35,13 @@ func (c *MagellanClient) Add(data util.HTTPBody, headers util.HTTPHeader) error
|
|||
return err
|
||||
}
|
||||
|
||||
func (c *MagellanClient) Update(data util.HTTPBody, headers util.HTTPHeader) error {
|
||||
func (c *MagellanClient) Update(data HTTPBody, headers HTTPHeader) error {
|
||||
if data == nil {
|
||||
return fmt.Errorf("no data found")
|
||||
}
|
||||
|
||||
path := "/inventory/update"
|
||||
res, body, err := util.MakeRequest(c.Client, path, http.MethodPut, data, headers)
|
||||
res, body, err := MakeRequest(c.Client, path, http.MethodPut, data, headers)
|
||||
if res != nil {
|
||||
statusOk := res.StatusCode >= 200 && res.StatusCode < 300
|
||||
if !statusOk {
|
||||
|
|
|
|||
178
pkg/client/net.go
Normal file
178
pkg/client/net.go
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// HTTP aliases for readibility
|
||||
type HTTPHeader map[string]string
|
||||
type HTTPBody []byte
|
||||
|
||||
func (h HTTPHeader) Authorization(accessToken string) HTTPHeader {
|
||||
if accessToken != "" {
|
||||
h["Authorization"] = fmt.Sprintf("Bearer %s", accessToken)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (h HTTPHeader) ContentType(contentType string) HTTPHeader {
|
||||
h["Content-Type"] = contentType
|
||||
return h
|
||||
}
|
||||
|
||||
// GetNextIP() returns the next IP address, but does not account
|
||||
// for net masks.
|
||||
func GetNextIP(ip *net.IP, inc uint) *net.IP {
|
||||
if ip == nil {
|
||||
return &net.IP{}
|
||||
}
|
||||
i := ip.To4()
|
||||
v := uint(i[0])<<24 + uint(i[1])<<16 + uint(i[2])<<8 + uint(i[3])
|
||||
v += inc
|
||||
v3 := byte(v & 0xFF)
|
||||
v2 := byte((v >> 8) & 0xFF)
|
||||
v1 := byte((v >> 16) & 0xFF)
|
||||
v0 := byte((v >> 24) & 0xFF)
|
||||
// return &net.IP{[]byte{v0, v1, v2, v3}}
|
||||
r := net.IPv4(v0, v1, v2, v3)
|
||||
return &r
|
||||
}
|
||||
|
||||
// MakeRequest() is a wrapper function that condenses simple HTTP
|
||||
// requests done to a single call. It expects an optional HTTP client,
|
||||
// URL, HTTP method, request body, and request headers. This function
|
||||
// is useful when making many requests where only these few arguments
|
||||
// are changing.
|
||||
//
|
||||
// Returns a HTTP response object, response body as byte array, and any
|
||||
// error that may have occurred with making the request.
|
||||
func MakeRequest(client *http.Client, url string, httpMethod string, body HTTPBody, header HTTPHeader) (*http.Response, HTTPBody, error) {
|
||||
// use defaults if no client provided
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest(httpMethod, url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create new HTTP request: %v", err)
|
||||
}
|
||||
req.Header.Add("User-Agent", "magellan")
|
||||
for k, v := range header {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to make request: %v", err)
|
||||
}
|
||||
b, err := io.ReadAll(res.Body)
|
||||
res.Body.Close()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read response body: %v", err)
|
||||
}
|
||||
return res, b, err
|
||||
}
|
||||
|
||||
// FormatHostUrls() takes a list of hosts and ports and builds full URLs in the
|
||||
// form of scheme://host:port. If no scheme is provided, it will use "https" by
|
||||
// default.
|
||||
//
|
||||
// Returns a 2D string slice where each slice contains URL host strings for each
|
||||
// port. The intention is to have all of the URLs for a single host combined into
|
||||
// a single slice to initiate one goroutine per host, but making request to multiple
|
||||
// ports.
|
||||
func FormatHostUrls(hosts []string, ports []int, scheme string, verbose bool) [][]string {
|
||||
// format each positional arg as a complete URL
|
||||
var formattedHosts [][]string
|
||||
for _, host := range hosts {
|
||||
uri, err := url.ParseRequestURI(host)
|
||||
if err != nil {
|
||||
if verbose {
|
||||
log.Warn().Msgf("invalid URI parsed: %s", host)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// check if scheme is set, if not set it with flag or default value ('https' if flag is not set)
|
||||
if uri.Scheme == "" {
|
||||
if scheme != "" {
|
||||
uri.Scheme = scheme
|
||||
} else {
|
||||
// hardcoded assumption
|
||||
uri.Scheme = "https"
|
||||
}
|
||||
}
|
||||
|
||||
// tidy up slashes and update arg with new value
|
||||
uri.Path = strings.TrimSuffix(uri.Path, "/")
|
||||
uri.Path = strings.ReplaceAll(uri.Path, "//", "/")
|
||||
|
||||
// for hosts with unspecified ports, add ports to scan from flag
|
||||
if uri.Port() == "" {
|
||||
var tmp []string
|
||||
for _, port := range ports {
|
||||
uri.Host += fmt.Sprintf(":%d", port)
|
||||
tmp = append(tmp, uri.String())
|
||||
}
|
||||
formattedHosts = append(formattedHosts, tmp)
|
||||
} else {
|
||||
formattedHosts = append(formattedHosts, []string{uri.String()})
|
||||
}
|
||||
|
||||
}
|
||||
return formattedHosts
|
||||
}
|
||||
|
||||
// FormatIPUrls() takes a list of IP addresses and ports and builds full URLs in the
|
||||
// form of scheme://host:port. If no scheme is provided, it will use "https" by
|
||||
// default.
|
||||
//
|
||||
// Returns a 2D string slice where each slice contains URL host strings for each
|
||||
// port. The intention is to have all of the URLs for a single host combined into
|
||||
// a single slice to initiate one goroutine per host, but making request to multiple
|
||||
// ports.
|
||||
func FormatIPUrls(ips []string, ports []int, scheme string, verbose bool) [][]string {
|
||||
// format each positional arg as a complete URL
|
||||
var formattedHosts [][]string
|
||||
for _, ip := range ips {
|
||||
if scheme == "" {
|
||||
scheme = "https"
|
||||
}
|
||||
// make an entirely new object since we're expecting just IPs
|
||||
uri := &url.URL{
|
||||
Scheme: scheme,
|
||||
Host: ip,
|
||||
}
|
||||
|
||||
// tidy up slashes and update arg with new value
|
||||
uri.Path = strings.ReplaceAll(uri.Path, "//", "/")
|
||||
uri.Path = strings.TrimSuffix(uri.Path, "/")
|
||||
|
||||
// for hosts with unspecified ports, add ports to scan from flag
|
||||
if uri.Port() == "" {
|
||||
if len(ports) == 0 {
|
||||
ports = append(ports, 443)
|
||||
}
|
||||
var tmp []string
|
||||
for _, port := range ports {
|
||||
uri.Host += fmt.Sprintf(":%d", port)
|
||||
tmp = append(tmp, uri.String())
|
||||
}
|
||||
formattedHosts = append(formattedHosts, tmp)
|
||||
} else {
|
||||
formattedHosts = append(formattedHosts, []string{uri.String()})
|
||||
}
|
||||
|
||||
}
|
||||
return formattedHosts
|
||||
}
|
||||
|
|
@ -6,8 +6,6 @@ package client
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/OpenCHAMI/magellan/internal/util"
|
||||
)
|
||||
|
||||
type SmdClient struct {
|
||||
|
|
@ -31,14 +29,14 @@ func (c SmdClient) GetClient() *http.Client {
|
|||
// Add() has a similar function definition to that of the default implementation,
|
||||
// but also allows further customization and data/header manipulation that would
|
||||
// be specific and/or unique to SMD's API.
|
||||
func (c SmdClient) Add(data util.HTTPBody, headers util.HTTPHeader) error {
|
||||
func (c SmdClient) Add(data HTTPBody, headers HTTPHeader) error {
|
||||
if data == nil {
|
||||
return fmt.Errorf("failed to add redfish endpoint: no data found")
|
||||
}
|
||||
|
||||
// Add redfish endpoint via POST `/hsm/v2/Inventory/RedfishEndpoints` endpoint
|
||||
url := c.RootEndpoint("/Inventory/RedfishEndpoints")
|
||||
res, body, err := util.MakeRequest(c.Client, url, http.MethodPost, data, headers)
|
||||
res, body, err := MakeRequest(c.Client, url, http.MethodPost, data, headers)
|
||||
if res != nil {
|
||||
statusOk := res.StatusCode >= 200 && res.StatusCode < 300
|
||||
if !statusOk {
|
||||
|
|
@ -49,13 +47,13 @@ func (c SmdClient) Add(data util.HTTPBody, headers util.HTTPHeader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (c SmdClient) Update(data util.HTTPBody, headers util.HTTPHeader) error {
|
||||
func (c SmdClient) Update(data HTTPBody, headers HTTPHeader) error {
|
||||
if data == nil {
|
||||
return fmt.Errorf("failed to add redfish endpoint: no data found")
|
||||
}
|
||||
// Update redfish endpoint via PUT `/hsm/v2/Inventory/RedfishEndpoints` endpoint
|
||||
url := c.RootEndpoint("/Inventory/RedfishEndpoints/" + c.Xname)
|
||||
res, body, err := util.MakeRequest(c.Client, url, http.MethodPut, data, headers)
|
||||
res, body, err := MakeRequest(c.Client, url, http.MethodPut, data, headers)
|
||||
fmt.Printf("%v (%v)\n%s\n", url, res.Status, string(body))
|
||||
if res != nil {
|
||||
statusOk := res.StatusCode >= 200 && res.StatusCode < 300
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue