* added GetIPAddresses function
* pagination is handled in a more sane way * removed unnecessary lines in devcontainer.json * renamed main.go to ipam.go
This commit is contained in:
parent
704670f604
commit
deacfad22d
@ -2,7 +2,6 @@
|
|||||||
// README at: https://github.com/devcontainers/templates/tree/main/src/go
|
// README at: https://github.com/devcontainers/templates/tree/main/src/go
|
||||||
{
|
{
|
||||||
"name": "Go",
|
"name": "Go",
|
||||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
||||||
"image": "mcr.microsoft.com/devcontainers/go:1.20",
|
"image": "mcr.microsoft.com/devcontainers/go:1.20",
|
||||||
|
|
||||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
@ -10,35 +9,10 @@
|
|||||||
"ghcr.io/guiyomh/features/vim": {}
|
"ghcr.io/guiyomh/features/vim": {}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Configure tool-specific properties.
|
|
||||||
"customizations": {
|
|
||||||
// Configure properties specific to VS Code.
|
|
||||||
"vscode": {
|
|
||||||
"settings": {},
|
|
||||||
"extensions": [
|
|
||||||
// "streetsidesoftware.code-spell-checker"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
||||||
// "forwardPorts": [9000],
|
|
||||||
|
|
||||||
// Use 'portsAttributes' to set default properties for specific forwarded ports.
|
|
||||||
// More info: https://containers.dev/implementors/json_reference/#port-attributes
|
|
||||||
// "portsAttributes": {
|
|
||||||
// "9000": {
|
|
||||||
// "label": "Hello Remote World",
|
|
||||||
// "onAutoForward": "notify"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
"postCreateCommand": {
|
"postCreateCommand": {
|
||||||
"download revive": "go install github.com/mgechev/revive@latest",
|
"download revive": "go install github.com/mgechev/revive@latest",
|
||||||
"add githook": "echo revive -set_exit_status > .git/hooks/pre-commit",
|
"add githook": "echo revive -set_exit_status > .git/hooks/pre-commit",
|
||||||
"chmod githook": "chmod +x .git/hooks/pre-commit"
|
"chmod githook": "chmod +x .git/hooks/pre-commit"
|
||||||
}
|
}
|
||||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
||||||
// "remoteUser": "root"
|
|
||||||
}
|
}
|
||||||
|
179
client.go
Normal file
179
client.go
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
// Package netboxapi provides custom functions for the default
|
||||||
|
// go-netbox package. It mostly handles pagination.
|
||||||
|
package netboxapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
e "git.stinnesbeck.com/nils/errorhandler"
|
||||||
|
transport "github.com/go-openapi/runtime/client"
|
||||||
|
"github.com/netbox-community/go-netbox/v3/netbox/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetBoxAPI is the type we use to add custom functions
|
||||||
|
type NetBoxAPI struct {
|
||||||
|
api client.NetBoxAPI
|
||||||
|
Token string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiResponse struct {
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
Next string `json:"next"`
|
||||||
|
Previous string `json:"previous"`
|
||||||
|
Results []any `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetBoxClient returns a client to NetBox at the given url,
|
||||||
|
// using the provided token
|
||||||
|
func NewNetBoxClient(token string, url string) (NetBoxAPI, error) {
|
||||||
|
// Construct the API basis and configure authentication
|
||||||
|
t := transport.New(url, client.DefaultBasePath, []string{"https"})
|
||||||
|
t.DefaultAuthentication = transport.APIKeyAuth(
|
||||||
|
"Authorization",
|
||||||
|
"header",
|
||||||
|
fmt.Sprintf("Token %v", token),
|
||||||
|
)
|
||||||
|
|
||||||
|
// create NetBox api variable
|
||||||
|
n := client.New(t, nil)
|
||||||
|
|
||||||
|
// create a new variable for our custom functions
|
||||||
|
nb := NetBoxAPI{
|
||||||
|
api: *n,
|
||||||
|
Token: token,
|
||||||
|
URL: url,
|
||||||
|
}
|
||||||
|
return nb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nb NetBoxAPI) handleRawPagination(url string) (*apiResponse, error) {
|
||||||
|
var response apiResponse
|
||||||
|
b, err := nb.rawNetBoxAPICall(url, http.MethodGet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, e.FormatError("can't perform raw NetBox API call", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal json into response
|
||||||
|
if err := json.Unmarshal(b, &response); err != nil {
|
||||||
|
return nil, e.FormatError("can't unmarshal json payload in raw NetBox API call", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// there are no more pages left, return the response we got
|
||||||
|
if response.Next == "" {
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// there are more pages left, get more responses from there
|
||||||
|
|
||||||
|
// recursive calling of handlePagination to get all following pages
|
||||||
|
nextResponse, err := nb.handleRawPagination(response.Next)
|
||||||
|
if err != nil {
|
||||||
|
return nil, e.FormatError("can't get next response from api", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// append all next responses' results to this response's result
|
||||||
|
response.Results = append(response.Results, nextResponse.Results...)
|
||||||
|
|
||||||
|
// go back up the chain through recursion
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nb NetBoxAPI) handlePagination(url string, payload any) error {
|
||||||
|
response, err := nb.handleRawPagination(url)
|
||||||
|
if err != nil {
|
||||||
|
return e.FormatError("can't handle pagination for url "+url, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(response.Count)
|
||||||
|
|
||||||
|
// cast response into our payload
|
||||||
|
if err := resultToOtherStructure(*response, &payload); err != nil {
|
||||||
|
return e.FormatError("can't parse result to other Structure", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prettify.Print(payload)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func resultToOtherStructure(input apiResponse, output any) error {
|
||||||
|
// get JSON representation of results
|
||||||
|
c, err := json.Marshal(input.Results)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal onto specific structure via output
|
||||||
|
return json.Unmarshal(c, &output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nb NetBoxAPI) rawNetBoxAPICall(url string, method string, data ...any) ([]byte, error) {
|
||||||
|
// construct complete URL (url already comes with a "/" after the "/api" part)
|
||||||
|
// url = fmt.Sprintf("https://%s/api%s", nb.URL, url)
|
||||||
|
|
||||||
|
// log.Printf("calling API on url: %s with method %s and data %+v\n", url, method, data)
|
||||||
|
// create new transport client
|
||||||
|
|
||||||
|
c := &http.Client{}
|
||||||
|
var req *http.Request
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case http.MethodPatch, http.MethodPost, http.MethodPut:
|
||||||
|
// to patch something we need data, check it exists
|
||||||
|
if data == nil {
|
||||||
|
return nil, fmt.Errorf("no data was provided, please provide data to use PATCH")
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, e.FormatError(fmt.Sprintf("can't marshal data %+v for apicall %s", data, url), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct request
|
||||||
|
innerRequest, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
|
||||||
|
if err != nil {
|
||||||
|
return nil, e.FormatError(fmt.Sprintf("can't construct new request (method: %s, URL: %s, payload %s)", method, url, payload), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hand over request to outer request
|
||||||
|
req = innerRequest
|
||||||
|
|
||||||
|
case http.MethodGet:
|
||||||
|
innerRequest, err := http.NewRequest(method, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, e.FormatError(fmt.Sprintf("can't construct new request (method: %s, URL: %s)", method, url), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hand over request to outer request
|
||||||
|
req = innerRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// set request headers
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Token %s", nb.Token))
|
||||||
|
|
||||||
|
// call API client with request
|
||||||
|
resp, err := c.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, e.FormatError("can't get response from "+url, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// read body of response
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, e.FormatError("can't read response body", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case 200, 201:
|
||||||
|
return body, nil
|
||||||
|
default:
|
||||||
|
return body, fmt.Errorf("got bad StatusCode %d in response from API, data is:\n\t-> %+v", resp.StatusCode, data)
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +1,13 @@
|
|||||||
// Package netboxapi provides custom functions for the default
|
|
||||||
// go-netbox package. It mostly handles pagination.
|
|
||||||
package netboxapi
|
package netboxapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
e "git.stinnesbeck.com/nils/errorhandler"
|
e "git.stinnesbeck.com/nils/errorhandler"
|
||||||
transport "github.com/go-openapi/runtime/client"
|
|
||||||
"github.com/netbox-community/go-netbox/v3/netbox/client"
|
|
||||||
"github.com/netbox-community/go-netbox/v3/netbox/client/ipam"
|
"github.com/netbox-community/go-netbox/v3/netbox/client/ipam"
|
||||||
"github.com/netbox-community/go-netbox/v3/netbox/models"
|
"github.com/netbox-community/go-netbox/v3/netbox/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NetBoxAPI is the type we use to add custom functions
|
|
||||||
type NetBoxAPI client.NetBoxAPI
|
|
||||||
|
|
||||||
// NewNetBoxClient returns a client to NetBox at the given url,
|
|
||||||
// using the provided token
|
|
||||||
func NewNetBoxClient(token string, url string) (NetBoxAPI, error) {
|
|
||||||
// Construct the API basis and configure authentication
|
|
||||||
t := transport.New(url, client.DefaultBasePath, []string{"https"})
|
|
||||||
t.DefaultAuthentication = transport.APIKeyAuth(
|
|
||||||
"Authorization",
|
|
||||||
"header",
|
|
||||||
fmt.Sprintf("Token %v", token),
|
|
||||||
)
|
|
||||||
|
|
||||||
// create NetBox api variable
|
|
||||||
n := client.New(t, nil)
|
|
||||||
|
|
||||||
// create a new variable for our custom functions
|
|
||||||
var nb NetBoxAPI = NetBoxAPI(*n)
|
|
||||||
return nb, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPrefixes returns prefixes from the api filtered by the parameter list param
|
// GetPrefixes returns prefixes from the api filtered by the parameter list param
|
||||||
func (nb NetBoxAPI) GetPrefixes(param ipam.IpamPrefixesListParams) ([]*models.Prefix, error) {
|
func (nb NetBoxAPI) GetPrefixes(param ipam.IpamPrefixesListParams) ([]*models.Prefix, error) {
|
||||||
// check if context was set, if not set background
|
// check if context was set, if not set background
|
||||||
@ -56,7 +29,7 @@ func (nb NetBoxAPI) GetPrefixes(param ipam.IpamPrefixesListParams) ([]*models.Pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get prefixes
|
// get prefixes
|
||||||
prefixes, err := nb.Ipam.IpamPrefixesList(¶m, nil)
|
prefixes, err := nb.api.Ipam.IpamPrefixesList(¶m, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, e.FormatError("can't get prefixes", err)
|
return nil, e.FormatError("can't get prefixes", err)
|
||||||
}
|
}
|
||||||
@ -88,3 +61,49 @@ func (nb NetBoxAPI) GetPrefixes(param ipam.IpamPrefixesListParams) ([]*models.Pr
|
|||||||
|
|
||||||
return prefixesToReturn, nil
|
return prefixesToReturn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetIPAddresses returns IP addresses from the api filtered by the parameter list param
|
||||||
|
func (nb NetBoxAPI) GetIPAddresses(param ipam.IpamIPAddressesListParams) ([]*models.IPAddress, error) {
|
||||||
|
// check if context was set, if not set background
|
||||||
|
if param.Context == nil {
|
||||||
|
param.Context = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultOffset int64
|
||||||
|
var defaultLimit int64 = 50
|
||||||
|
|
||||||
|
// set Offset if not set before
|
||||||
|
if param.Limit == nil {
|
||||||
|
param.Limit = &defaultLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
// set Offset if not set before
|
||||||
|
if param.Offset == nil {
|
||||||
|
param.Offset = &defaultOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
// get addresses
|
||||||
|
addresses, err := nb.api.Ipam.IpamIPAddressesList(¶m, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, e.FormatError("can't get prefixes", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if there are no more prefixes on other pages
|
||||||
|
if addresses.Payload.Next == nil {
|
||||||
|
// return the results of the first batch, there are no others!
|
||||||
|
return addresses.Payload.Results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// variable to hold all prefixes to return
|
||||||
|
var addressesToReturn []*models.IPAddress
|
||||||
|
|
||||||
|
if err := nb.handlePagination(addresses.Payload.Next.String(), &addressesToReturn); err != nil {
|
||||||
|
return nil, e.FormatError("can't handle Pagination", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// append append first batch to the other batches
|
||||||
|
addressesToReturn = append(addressesToReturn, addresses.Payload.Results...)
|
||||||
|
|
||||||
|
// return all addresses
|
||||||
|
return addressesToReturn, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user