package netbox import ( "context" "encoding/json" "fmt" "io" "net" "net/http" "net/url" "strconv" "code.crute.us/mcrute/golib/vault" ) type NetboxClient interface { GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) GetPrefixesWithTag(ctx context.Context, tag string) ([]*net.IPNet, error) } func NewNetboxClient(endpoint string, vault vault.VaultClient, vaultMaterial string) NetboxClient { return &netboxClient{ endpoint: endpoint, vault: vault, vaultMaterial: vaultMaterial, } } type netboxClient struct { vault vault.VaultClient endpoint string vaultMaterial string } func (c *netboxClient) makeRequestRaw(ctx context.Context, method, u string, ib io.Reader, o interface{}) error { apiKey, err := c.vault.KVApiKey(ctx, c.vaultMaterial) if err != nil { return err } req, err := http.NewRequestWithContext(ctx, method, u, ib) if err != nil { return err } req.Header.Add("Authorization", fmt.Sprintf("Token %s", apiKey.Key)) 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(ctx context.Context, 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(ctx, method, u.String(), ib, o) } func (c *netboxClient) GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) { s, err := c.resolveSiteNameToId(ctx, 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(ctx, 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(ctx, 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(ctx context.Context, tag string) ([]*net.IPNet, error) { out := []*net.IPNet{} q := url.Values{} q.Add("tag", tag) page := &PrefixList{} if err := c.makeRequest(ctx, 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(ctx, 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(ctx context.Context, s string) (int, error) { q := url.Values{} q.Add("name", s) out := &SiteList{} if err := c.makeRequest(ctx, 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 }