2019-09-11 20:12:42 +02:00
|
|
|
package netboxgo
|
2019-09-10 22:32:20 +02:00
|
|
|
|
2019-12-17 16:35:52 +01:00
|
|
|
import (
|
2021-11-26 11:09:27 +01:00
|
|
|
"bytes"
|
|
|
|
"context"
|
2019-12-17 16:35:52 +01:00
|
|
|
"crypto/tls"
|
|
|
|
"encoding/json"
|
2021-11-26 11:09:27 +01:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log"
|
2019-12-17 16:35:52 +01:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2021-11-26 11:09:27 +01:00
|
|
|
// Client struct is used to create a new NetBox endpoint
|
|
|
|
type Client struct {
|
2022-02-14 14:12:46 +01:00
|
|
|
Clusters *ClustersService
|
|
|
|
ClusterTypes *ClusterTypesService
|
|
|
|
Devices *DevicesService
|
|
|
|
DeviceRoles *DeviceRolesService
|
|
|
|
DeviceTypes *DeviceTypesService
|
|
|
|
DCIMInterfaces *DCIMInterfacesService
|
|
|
|
InventoryItems *InventoryItemsService
|
|
|
|
Secrets *SecretsService
|
|
|
|
Sites *SitesService
|
|
|
|
SiteGroups *SiteGroupsService
|
|
|
|
Platforms *PlatformsService
|
|
|
|
Prefixes *PrefixesService
|
|
|
|
RearPorts *RearPortsService
|
|
|
|
Tenants *TenantsService
|
|
|
|
VirtualMachines *VirtualMachinesService
|
|
|
|
VirtualizationInterfaces *VirtualizationInterfacesService
|
|
|
|
VRFs *VRFsService
|
|
|
|
VLANs *VLANsService
|
2021-11-26 11:09:27 +01:00
|
|
|
|
|
|
|
// baseURL is the URL used for the base URL of the API
|
|
|
|
baseURL *url.URL
|
|
|
|
|
|
|
|
// httpClient is used for HTTP requests
|
|
|
|
httpClient *http.Client
|
|
|
|
|
|
|
|
// Reuse a single struct instead of allocating one for each service on the heap.
|
|
|
|
common service
|
|
|
|
|
|
|
|
// UserAgent is used to set the UserAgent on the HTTP requests
|
|
|
|
UserAgent string
|
|
|
|
|
|
|
|
// Token is set for authentication of the API
|
|
|
|
Token string
|
|
|
|
|
2021-11-27 13:10:56 +01:00
|
|
|
// TokenPrefix is optional to set token prefix other than "Token"
|
|
|
|
TokenPrefix string
|
|
|
|
|
2021-11-26 11:09:27 +01:00
|
|
|
// SessionKey is used to read authentication data
|
|
|
|
SessionKey string
|
|
|
|
|
|
|
|
// Used by golang wasm
|
|
|
|
FetchMode string
|
2021-11-26 13:21:19 +01:00
|
|
|
|
|
|
|
// Debug enables verbose debugging messages to console.
|
|
|
|
Debug bool
|
|
|
|
|
|
|
|
// InsecureSkipVerify is used to selectively skip InsecureVerifications
|
|
|
|
InsecureSkipVerify bool
|
2021-11-26 11:09:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type service struct {
|
|
|
|
client *Client
|
2019-09-11 09:19:24 +02:00
|
|
|
}
|
|
|
|
|
2021-11-27 23:31:44 +01:00
|
|
|
const (
|
|
|
|
circuitsPath = "/circuits"
|
|
|
|
dcimPath = "/dcim"
|
|
|
|
extrasPath = "/extras"
|
|
|
|
ipamPath = "/ipam"
|
|
|
|
tenancyPath = "/tenancy"
|
|
|
|
usersPath = "/users"
|
|
|
|
virtualizationPath = "/virtualization"
|
|
|
|
)
|
|
|
|
|
2020-03-02 10:00:40 +01:00
|
|
|
// NetBoxSessionKey sets the session key for secrets retrieval
|
2019-12-17 16:35:52 +01:00
|
|
|
type NetBoxSessionKey struct {
|
|
|
|
XSessionKey string `json:"session_key"`
|
|
|
|
}
|
|
|
|
|
2021-11-26 11:09:27 +01:00
|
|
|
// NewClient returns a new NetBox API client
|
|
|
|
func NewClient(apiurl string, httpClient *http.Client) (*Client, error) {
|
|
|
|
c := &Client{}
|
|
|
|
|
|
|
|
api, err := url.Parse(apiurl)
|
|
|
|
if err != nil {
|
|
|
|
return c, fmt.Errorf("unable to parse url %s: %w", apiurl, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.baseURL = api
|
|
|
|
c.SetDebug(false)
|
|
|
|
c.SetUserAgent("netboxgo/0.0.6")
|
|
|
|
c.httpClient = httpClient
|
|
|
|
|
|
|
|
if httpClient == nil {
|
|
|
|
transport := &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
InsecureSkipVerify: false,
|
|
|
|
CipherSuites: []uint16{
|
|
|
|
tls.TLS_AES_128_GCM_SHA256,
|
|
|
|
tls.TLS_AES_256_GCM_SHA384,
|
2021-11-26 13:39:29 +01:00
|
|
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
2021-11-26 11:09:27 +01:00
|
|
|
tls.TLS_CHACHA20_POLY1305_SHA256,
|
|
|
|
},
|
|
|
|
MinVersion: tls.VersionTLS12,
|
|
|
|
},
|
|
|
|
Proxy: http.ProxyFromEnvironment,
|
|
|
|
}
|
|
|
|
|
|
|
|
timeout := time.Duration(60 * time.Second)
|
|
|
|
c.httpClient = &http.Client{
|
|
|
|
Transport: transport,
|
|
|
|
Jar: nil,
|
|
|
|
Timeout: timeout,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.common.client = c
|
2021-11-26 13:34:15 +01:00
|
|
|
c.Clusters = (*ClustersService)(&c.common)
|
2022-02-09 10:42:44 +01:00
|
|
|
c.ClusterTypes = (*ClusterTypesService)(&c.common)
|
2021-11-26 13:34:15 +01:00
|
|
|
c.Devices = (*DevicesService)(&c.common)
|
|
|
|
c.DeviceRoles = (*DeviceRolesService)(&c.common)
|
|
|
|
c.DeviceTypes = (*DeviceTypesService)(&c.common)
|
2022-02-14 14:12:46 +01:00
|
|
|
c.DCIMInterfaces = (*DCIMInterfacesService)(&c.common)
|
|
|
|
c.VirtualizationInterfaces = (*VirtualizationInterfacesService)(&c.common)
|
2021-11-26 13:34:15 +01:00
|
|
|
c.InventoryItems = (*InventoryItemsService)(&c.common)
|
|
|
|
c.Prefixes = (*PrefixesService)(&c.common)
|
2022-02-09 10:42:44 +01:00
|
|
|
c.Platforms = (*PlatformsService)(&c.common)
|
2021-11-26 13:34:15 +01:00
|
|
|
c.RearPorts = (*RearPortsService)(&c.common)
|
|
|
|
c.Tenants = (*TenantsService)(&c.common)
|
|
|
|
c.Secrets = (*SecretsService)(&c.common)
|
|
|
|
c.Sites = (*SitesService)(&c.common)
|
|
|
|
c.SiteGroups = (*SiteGroupsService)(&c.common)
|
|
|
|
c.VirtualMachines = (*VirtualMachinesService)(&c.common)
|
|
|
|
c.VLANs = (*VLANsService)(&c.common)
|
|
|
|
c.VRFs = (*VRFsService)(&c.common)
|
2021-11-26 11:09:27 +01:00
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetUserAgent is used to set the user agent for interaction with the API
|
|
|
|
func (c *Client) SetUserAgent(u string) {
|
|
|
|
c.UserAgent = u
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetDebug is used to toggle debugging, set to true for debug
|
|
|
|
func (c *Client) SetDebug(b bool) {
|
|
|
|
c.Debug = b
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetToken is used to set the http token bearer for interaction with the API
|
|
|
|
func (c *Client) SetToken(token string) {
|
|
|
|
c.Token = token
|
2019-09-10 22:32:20 +02:00
|
|
|
}
|
2019-12-17 16:35:52 +01:00
|
|
|
|
2021-11-26 11:09:27 +01:00
|
|
|
// newRequest is used to make new request to endpoints
|
|
|
|
func (c *Client) newRequest(ctx context.Context, method, path string, query string, body interface{}) (*http.Request, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
var buf io.ReadWriter
|
|
|
|
|
|
|
|
u := &url.URL{
|
|
|
|
Scheme: c.baseURL.Scheme,
|
|
|
|
Host: c.baseURL.Host,
|
|
|
|
Path: c.baseURL.Path + path,
|
|
|
|
RawQuery: query,
|
|
|
|
}
|
|
|
|
|
|
|
|
if body != nil {
|
|
|
|
buf = new(bytes.Buffer)
|
|
|
|
|
|
|
|
err = json.NewEncoder(buf).Encode(body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
readBuf, _ := json.Marshal(body)
|
|
|
|
c.debugf("debug: method:%s url:%s body:%s", method, u.String(), string(readBuf))
|
|
|
|
|
|
|
|
req, err := http.NewRequestWithContext(ctx, method, u.String(), buf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if body != nil {
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.SessionKey != "" {
|
|
|
|
req.Header.Add("X-Session-Key", c.SessionKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
if c.UserAgent != "" {
|
|
|
|
req.Header.Set("User-Agent", c.UserAgent)
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Token != "" {
|
2021-11-27 13:10:56 +01:00
|
|
|
req.Header.Add("Authorization", "Token "+c.Token)
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Token != "" && c.TokenPrefix != "" {
|
|
|
|
req.Header.Add("Authorization", c.TokenPrefix+" "+c.Token)
|
2021-11-26 11:09:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if c.SessionKey != "" {
|
|
|
|
req.Header.Add("X-Session-Key", c.SessionKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// do method is used to decode request response to proper struct
|
|
|
|
func (c *Client) do(req *http.Request, v interface{}) (*http.Response, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
var resp *http.Response
|
|
|
|
|
|
|
|
resp, err = c.httpClient.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("client unable to do request: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
|
|
|
var data []byte
|
|
|
|
|
|
|
|
data, err = io.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to read body response: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("request failed: body=%s", string(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
if v != nil {
|
|
|
|
err = json.NewDecoder(resp.Body).Decode(v)
|
|
|
|
if err != nil {
|
|
|
|
return resp, fmt.Errorf("unable to decode response body: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// debugf is used as a formtter for debugging information
|
|
|
|
func (c *Client) debugf(fmt string, v ...interface{}) {
|
|
|
|
if c.Debug {
|
2022-02-09 10:42:44 +01:00
|
|
|
log.Printf("netbox client: "+fmt, v...)
|
2021-11-26 11:09:27 +01:00
|
|
|
}
|
|
|
|
}
|