diff options
Diffstat (limited to 'clients/netbox/client.go')
-rw-r--r-- | clients/netbox/client.go | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/clients/netbox/client.go b/clients/netbox/client.go new file mode 100644 index 0000000..905aa40 --- /dev/null +++ b/clients/netbox/client.go | |||
@@ -0,0 +1,165 @@ | |||
1 | package netbox | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "encoding/json" | ||
6 | "fmt" | ||
7 | "io" | ||
8 | "net" | ||
9 | "net/http" | ||
10 | "net/url" | ||
11 | "strconv" | ||
12 | |||
13 | "code.crute.us/mcrute/golib/vault" | ||
14 | ) | ||
15 | |||
16 | type NetboxClient interface { | ||
17 | GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) | ||
18 | GetPrefixesWithTag(ctx context.Context, tag string) ([]*net.IPNet, error) | ||
19 | } | ||
20 | |||
21 | func NewNetboxClient(endpoint string, vault vault.VaultClient, vaultMaterial string) NetboxClient { | ||
22 | return &netboxClient{ | ||
23 | endpoint: endpoint, | ||
24 | vault: vault, | ||
25 | vaultMaterial: vaultMaterial, | ||
26 | } | ||
27 | } | ||
28 | |||
29 | type netboxClient struct { | ||
30 | vault vault.VaultClient | ||
31 | endpoint string | ||
32 | vaultMaterial string | ||
33 | } | ||
34 | |||
35 | func (c *netboxClient) makeRequestRaw(ctx context.Context, method, u string, ib io.Reader, o interface{}) error { | ||
36 | apiKey, err := c.vault.KVApiKey(ctx, c.vaultMaterial) | ||
37 | if err != nil { | ||
38 | return err | ||
39 | } | ||
40 | |||
41 | req, err := http.NewRequestWithContext(ctx, method, u, ib) | ||
42 | if err != nil { | ||
43 | return err | ||
44 | } | ||
45 | req.Header.Add("Authorization", fmt.Sprintf("Token %s", apiKey.Key)) | ||
46 | |||
47 | res, err := http.DefaultClient.Do(req) | ||
48 | if err != nil { | ||
49 | return err | ||
50 | } | ||
51 | defer res.Body.Close() | ||
52 | |||
53 | if err = json.NewDecoder(res.Body).Decode(o); err != nil { | ||
54 | return err | ||
55 | } | ||
56 | |||
57 | return nil | ||
58 | } | ||
59 | |||
60 | func (c *netboxClient) makeRequest(ctx context.Context, method, path string, q url.Values, ib io.Reader, o interface{}) error { | ||
61 | u, err := url.Parse(c.endpoint) | ||
62 | if err != nil { | ||
63 | return err | ||
64 | } | ||
65 | u.Path = path | ||
66 | u.RawQuery = q.Encode() | ||
67 | |||
68 | return c.makeRequestRaw(ctx, method, u.String(), ib, o) | ||
69 | } | ||
70 | |||
71 | func (c *netboxClient) GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) { | ||
72 | s, err := c.resolveSiteNameToId(ctx, site) | ||
73 | if err != nil { | ||
74 | return nil, err | ||
75 | } | ||
76 | |||
77 | out := []*net.IPNet{} | ||
78 | |||
79 | q := url.Values{} | ||
80 | q.Add("site_id", strconv.Itoa(s)) | ||
81 | q.Add("tag", tag) | ||
82 | |||
83 | page := &PrefixList{} | ||
84 | if err := c.makeRequest(ctx, http.MethodGet, "/api/ipam/prefixes/", q, nil, page); err != nil { | ||
85 | return nil, err | ||
86 | } | ||
87 | |||
88 | for _, r := range page.Results { | ||
89 | _, ipnet, err := net.ParseCIDR(r.Prefix) | ||
90 | if err != nil { | ||
91 | return nil, err | ||
92 | } | ||
93 | out = append(out, ipnet) | ||
94 | } | ||
95 | |||
96 | for page.Next != "" { | ||
97 | page = &PrefixList{} | ||
98 | if err := c.makeRequestRaw(ctx, http.MethodGet, page.Next, nil, page); err != nil { | ||
99 | return nil, err | ||
100 | } | ||
101 | |||
102 | for _, r := range page.Results { | ||
103 | _, ipnet, err := net.ParseCIDR(r.Prefix) | ||
104 | if err != nil { | ||
105 | return nil, err | ||
106 | } | ||
107 | out = append(out, ipnet) | ||
108 | } | ||
109 | } | ||
110 | |||
111 | return out, nil | ||
112 | } | ||
113 | |||
114 | func (c *netboxClient) GetPrefixesWithTag(ctx context.Context, tag string) ([]*net.IPNet, error) { | ||
115 | out := []*net.IPNet{} | ||
116 | |||
117 | q := url.Values{} | ||
118 | q.Add("tag", tag) | ||
119 | |||
120 | page := &PrefixList{} | ||
121 | if err := c.makeRequest(ctx, http.MethodGet, "/api/ipam/prefixes/", q, nil, page); err != nil { | ||
122 | return nil, err | ||
123 | } | ||
124 | |||
125 | for _, r := range page.Results { | ||
126 | _, ipnet, err := net.ParseCIDR(r.Prefix) | ||
127 | if err != nil { | ||
128 | return nil, err | ||
129 | } | ||
130 | out = append(out, ipnet) | ||
131 | } | ||
132 | |||
133 | for page.Next != "" { | ||
134 | page = &PrefixList{} | ||
135 | if err := c.makeRequestRaw(ctx, http.MethodGet, page.Next, nil, page); err != nil { | ||
136 | return nil, err | ||
137 | } | ||
138 | |||
139 | for _, r := range page.Results { | ||
140 | _, ipnet, err := net.ParseCIDR(r.Prefix) | ||
141 | if err != nil { | ||
142 | return nil, err | ||
143 | } | ||
144 | out = append(out, ipnet) | ||
145 | } | ||
146 | } | ||
147 | |||
148 | return out, nil | ||
149 | } | ||
150 | |||
151 | func (c *netboxClient) resolveSiteNameToId(ctx context.Context, s string) (int, error) { | ||
152 | q := url.Values{} | ||
153 | q.Add("name", s) | ||
154 | |||
155 | out := &SiteList{} | ||
156 | if err := c.makeRequest(ctx, http.MethodGet, "/api/dcim/sites/", q, nil, out); err != nil { | ||
157 | return 0, err | ||
158 | } | ||
159 | |||
160 | if len(out.Results) == 0 { | ||
161 | return 0, fmt.Errorf("resolveSiteNameToId: no results returned from netbox") | ||
162 | } | ||
163 | |||
164 | return out.Results[0].ID, nil | ||
165 | } | ||