aboutsummaryrefslogtreecommitdiff
path: root/clients/netbox/config_file_client.go
diff options
context:
space:
mode:
Diffstat (limited to 'clients/netbox/config_file_client.go')
-rw-r--r--clients/netbox/config_file_client.go151
1 files changed, 151 insertions, 0 deletions
diff --git a/clients/netbox/config_file_client.go b/clients/netbox/config_file_client.go
new file mode 100644
index 0000000..6023916
--- /dev/null
+++ b/clients/netbox/config_file_client.go
@@ -0,0 +1,151 @@
1package netbox
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "io/fs"
8 "net"
9 "path/filepath"
10
11 "github.com/mitchellh/mapstructure"
12 "gopkg.in/yaml.v2"
13)
14
15type ConfigFileNetboxClient struct {
16 cfg map[string]any
17}
18
19var _ NetboxClient = (*ConfigFileNetboxClient)(nil)
20
21// NewConfigFileClient creates a new ConfigFileNetboxClient by loading a
22// named config file from a filesystem and unmarshalling it. The config
23// file can be in JSON or YAML format, determined by a .json, .yaml, or
24// .yml extension. The configuration must be nested within a key in that
25// file to support sharing the file with other subsystems.
26//
27// See method docs for the extected format of this file.
28func NewConfigFileClient(filesystem fs.FS, name, key string) (NetboxClient, error) {
29 fp, err := filesystem.Open(name)
30 if err != nil {
31 return nil, err
32 }
33 defer fp.Close()
34
35 fc := map[string]any{}
36
37 switch e := filepath.Ext(name); e {
38 case ".yaml", ".yml":
39 if err := yaml.NewDecoder(fp).Decode(&fc); err != nil {
40 return nil, err
41 }
42 case ".json":
43 if err := json.NewDecoder(fp).Decode(&fc); err != nil {
44 return nil, err
45 }
46 default:
47 return nil, fmt.Errorf("Config files with extension %s are not supported", e)
48 }
49
50 ck, ok := fc[key]
51 if !ok {
52 return nil, fmt.Errorf("Key %s does not exist in config file", key)
53 }
54
55 cfg := map[string]any{}
56 if err := mapstructure.Decode(ck, &cfg); err != nil {
57 return nil, fmt.Errorf("Config file key was not of correct type: %w", err)
58 }
59
60 return &ConfigFileNetboxClient{
61 cfg: cfg,
62 }, nil
63}
64
65// GetSitePrefixesWithTag gets a list of IP prefixes for a site based on
66// some tag.
67//
68// The data format of the config file should be, under the key "sites":
69//
70// map[string]map[string][]string{
71// "site_name": map[string][]string{
72// "tag_name": []string{
73// "127.0.0.1/24",
74// },
75// },
76// }
77//
78// In JSON format:
79//
80// { "sites": {
81// "site": {
82// "tag_name": [ "127.0.0.1/24" ]
83// }
84// }
85func (c *ConfigFileNetboxClient) GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) {
86 s, ok := c.cfg["sites"]
87 if !ok {
88 return nil, fmt.Errorf("No key 'sites' in config file")
89 }
90
91 sites := map[string]map[string][]string{}
92 if err := mapstructure.Decode(s, &sites); err != nil {
93 return nil, fmt.Errorf("Key 'sites' not in correct format: %w", err)
94 }
95
96 tags, ok := sites[site]
97 if !ok {
98 return nil, fmt.Errorf("Site %s not in sites", site)
99 }
100
101 values, ok := tags[tag]
102 if !ok {
103 return nil, fmt.Errorf("Tag %s not in tags", tag)
104 }
105
106 return parseValues(values)
107}
108
109// GetPrefixesWithTag gets a list fo IP prefixes based on some tag.
110//
111// The data format of the config file should be, under the key "tags":
112//
113// map[string][]string{
114// "tag_name": []string{
115// "127.0.0.1/24",
116// },
117// }
118//
119// In JSON format:
120//
121// { "tags": { "tag_name": [ "127.0.0.1/24" ] } }
122func (c *ConfigFileNetboxClient) GetPrefixesWithTag(ctx context.Context, tag string) ([]*net.IPNet, error) {
123 t, ok := c.cfg["tags"]
124 if !ok {
125 return nil, fmt.Errorf("No key 'tags' in config file")
126 }
127
128 tags := map[string][]string{}
129 if err := mapstructure.Decode(t, &tags); err != nil {
130 return nil, fmt.Errorf("Key 'tags' not in correct format: %w", err)
131 }
132
133 values, ok := tags[tag]
134 if !ok {
135 return nil, fmt.Errorf("Tag %s not in tags", tag)
136 }
137
138 return parseValues(values)
139}
140
141func parseValues(v []string) ([]*net.IPNet, error) {
142 out := make([]*net.IPNet, len(v))
143 for i, n := range v {
144 _, in, err := net.ParseCIDR(n)
145 if err != nil {
146 return nil, err
147 }
148 out[i] = in
149 }
150 return out, nil
151}