package netbox import ( "encoding/json" "fmt" "io" "net" "net/http" "net/url" "strconv" "code.crute.us/mcrute/golib/vault" ) type NetboxClient interface { GetSitePrefixesWithTag(site string, tag string) ([]*net.IPNet, error) GetPrefixesWithTag(tag string) ([]*net.IPNet, error) } func NewNetboxClientDefault() NetboxClient { return &netboxClient{ endpoint: "https://netbox.crute.me", vaultMaterial: "infra/netbox-readonly", } } func NewNetboxClient(endpoint string, vaultMaterial string) NetboxClient { return &netboxClient{ endpoint: endpoint, vaultMaterial: vaultMaterial, } } type netboxClient struct { endpoint string vaultMaterial string } func (c *netboxClient) makeRequestRaw(method, u string, ib io.Reader, o interface{}) error { apiKey, err := vault.GetVaultApiKey(c.vaultMaterial) if err != nil { return err } req, err := http.NewRequest(method, u, ib) if err != nil { return err } req.Header.Add("Authorization", fmt.Sprintf("Token %s", apiKey)) res, err := http.DefaultClient.Do(req) if err != nil { return err } defer res.Body.Close() if err = json.NewDecoder(res.Body).Decode(o); err != nil { return err } return nil } func (c *netboxClient) makeRequest(method, path string, q url.Values, ib io.Reader, o interface{}) error { u, err := url.Parse(c.endpoint) if err != nil { return err } u.Path = path u.RawQuery = q.Encode() return c.makeRequestRaw(method, u.String(), ib, o) } func (c *netboxClient) GetSitePrefixesWithTag(site string, tag string) ([]*net.IPNet, error) { s, err := c.resolveSiteNameToId(site) if err != nil { return nil, err } out := []*net.IPNet{} q := url.Values{} q.Add("site_id", strconv.Itoa(s)) q.Add("tag", tag) page := &PrefixList{} if err := c.makeRequest(http.MethodGet, "/api/ipam/prefixes/", q, nil, page); err != nil { return nil, err } for _, r := range page.Results { _, ipnet, err := net.ParseCIDR(r.Prefix) if err != nil { return nil, err } out = append(out, ipnet) } for page.Next != "" { page = &PrefixList{} if err := c.makeRequestRaw(http.MethodGet, page.Next, nil, page); err != nil { return nil, err } for _, r := range page.Results { _, ipnet, err := net.ParseCIDR(r.Prefix) if err != nil { return nil, err } out = append(out, ipnet) } } return out, nil } func (c *netboxClient) GetPrefixesWithTag(tag string) ([]*net.IPNet, error) { out := []*net.IPNet{} q := url.Values{} q.Add("tag", tag) page := &PrefixList{} if err := c.makeRequest(http.MethodGet, "/api/ipam/prefixes/", q, nil, page); err != nil { return nil, err } for _, r := range page.Results { _, ipnet, err := net.ParseCIDR(r.Prefix) if err != nil { return nil, err } out = append(out, ipnet) } for page.Next != "" { page = &PrefixList{} if err := c.makeRequestRaw(http.MethodGet, page.Next, nil, page); err != nil { return nil, err } for _, r := range page.Results { _, ipnet, err := net.ParseCIDR(r.Prefix) if err != nil { return nil, err } out = append(out, ipnet) } } return out, nil } func (c *netboxClient) resolveSiteNameToId(s string) (int, error) { q := url.Values{} q.Add("name", s) out := &SiteList{} if err := c.makeRequest(http.MethodGet, "/api/dcim/sites/", q, nil, out); err != nil { return 0, err } if len(out.Results) == 0 { return 0, fmt.Errorf("resolveSiteNameToId: no results returned from netbox") } return out.Results[0].ID, nil }