diff options
author | Mike Crute <mike@crute.us> | 2023-08-01 17:39:13 -0700 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2023-08-01 17:39:13 -0700 |
commit | 7fbdc96ad276b7a24fca428e42578494c0dcc35b (patch) | |
tree | 1b5521e5eba6a2c6baa090f193fa1d116762763c | |
parent | 058e4faba9c1531661234a5adaa7a6c3791f92d4 (diff) | |
download | golib-7fbdc96ad276b7a24fca428e42578494c0dcc35b.tar.bz2 golib-7fbdc96ad276b7a24fca428e42578494c0dcc35b.tar.xz golib-7fbdc96ad276b7a24fca428e42578494c0dcc35b.zip |
netbox: add GetServicesForVm callclients/netbox/v3.3.0
-rw-r--r-- | clients/netbox/client.go | 30 | ||||
-rw-r--r-- | clients/netbox/config_file_client.go | 68 | ||||
-rw-r--r-- | clients/netbox/config_file_client_test.go | 26 | ||||
-rw-r--r-- | clients/netbox/model.go | 73 |
4 files changed, 179 insertions, 18 deletions
diff --git a/clients/netbox/client.go b/clients/netbox/client.go index 0cad733..ad2967a 100644 --- a/clients/netbox/client.go +++ b/clients/netbox/client.go | |||
@@ -14,6 +14,7 @@ import ( | |||
14 | type NetboxClient interface { | 14 | type NetboxClient interface { |
15 | GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) | 15 | GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) |
16 | GetPrefixesWithTag(ctx context.Context, tag string) ([]*net.IPNet, error) | 16 | GetPrefixesWithTag(ctx context.Context, tag string) ([]*net.IPNet, error) |
17 | GetServicesForVm(ctx context.Context, vmName string) ([]*Service, error) | ||
17 | } | 18 | } |
18 | 19 | ||
19 | type BasicNetboxClient struct { | 20 | type BasicNetboxClient struct { |
@@ -157,3 +158,32 @@ func (c *BasicNetboxClient) resolveSiteNameToId(ctx context.Context, s string) ( | |||
157 | 158 | ||
158 | return out.Results[0].ID, nil | 159 | return out.Results[0].ID, nil |
159 | } | 160 | } |
161 | |||
162 | func (c *BasicNetboxClient) GetServicesForVm(ctx context.Context, vmName string) ([]*Service, error) { | ||
163 | out := []*Service{} | ||
164 | |||
165 | q := url.Values{} | ||
166 | q.Add("virtual_machine", vmName) | ||
167 | |||
168 | page := &ServiceList{} | ||
169 | if err := c.makeRequest(ctx, http.MethodGet, "/api/ipam/services/", q, nil, page); err != nil { | ||
170 | return nil, err | ||
171 | } | ||
172 | |||
173 | for _, r := range page.Results { | ||
174 | out = append(out, r) | ||
175 | } | ||
176 | |||
177 | for page.Next != "" { | ||
178 | page = &ServiceList{} | ||
179 | if err := c.makeRequestRaw(ctx, http.MethodGet, page.Next, nil, page); err != nil { | ||
180 | return nil, err | ||
181 | } | ||
182 | |||
183 | for _, r := range page.Results { | ||
184 | out = append(out, r) | ||
185 | } | ||
186 | } | ||
187 | |||
188 | return out, nil | ||
189 | } | ||
diff --git a/clients/netbox/config_file_client.go b/clients/netbox/config_file_client.go index 6023916..d84dd80 100644 --- a/clients/netbox/config_file_client.go +++ b/clients/netbox/config_file_client.go | |||
@@ -67,21 +67,21 @@ func NewConfigFileClient(filesystem fs.FS, name, key string) (NetboxClient, erro | |||
67 | // | 67 | // |
68 | // The data format of the config file should be, under the key "sites": | 68 | // The data format of the config file should be, under the key "sites": |
69 | // | 69 | // |
70 | // map[string]map[string][]string{ | 70 | // map[string]map[string][]string{ |
71 | // "site_name": map[string][]string{ | 71 | // "site_name": map[string][]string{ |
72 | // "tag_name": []string{ | 72 | // "tag_name": []string{ |
73 | // "127.0.0.1/24", | 73 | // "127.0.0.1/24", |
74 | // }, | 74 | // }, |
75 | // }, | 75 | // }, |
76 | // } | 76 | // } |
77 | // | 77 | // |
78 | // In JSON format: | 78 | // In JSON format: |
79 | // | 79 | // |
80 | // { "sites": { | 80 | // { "sites": { |
81 | // "site": { | 81 | // "site": { |
82 | // "tag_name": [ "127.0.0.1/24" ] | 82 | // "tag_name": [ "127.0.0.1/24" ] |
83 | // } | 83 | // } |
84 | // } | 84 | // } |
85 | func (c *ConfigFileNetboxClient) GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) { | 85 | func (c *ConfigFileNetboxClient) GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) { |
86 | s, ok := c.cfg["sites"] | 86 | s, ok := c.cfg["sites"] |
87 | if !ok { | 87 | if !ok { |
@@ -110,11 +110,11 @@ func (c *ConfigFileNetboxClient) GetSitePrefixesWithTag(ctx context.Context, sit | |||
110 | // | 110 | // |
111 | // The data format of the config file should be, under the key "tags": | 111 | // The data format of the config file should be, under the key "tags": |
112 | // | 112 | // |
113 | // map[string][]string{ | 113 | // map[string][]string{ |
114 | // "tag_name": []string{ | 114 | // "tag_name": []string{ |
115 | // "127.0.0.1/24", | 115 | // "127.0.0.1/24", |
116 | // }, | 116 | // }, |
117 | // } | 117 | // } |
118 | // | 118 | // |
119 | // In JSON format: | 119 | // In JSON format: |
120 | // | 120 | // |
@@ -149,3 +149,37 @@ func parseValues(v []string) ([]*net.IPNet, error) { | |||
149 | } | 149 | } |
150 | return out, nil | 150 | return out, nil |
151 | } | 151 | } |
152 | |||
153 | // GetServicesForVm returns a list of services for a named VM | ||
154 | // | ||
155 | // The data format of the config file should be, under the key "services": | ||
156 | // | ||
157 | // map[string]map[string][]string{ | ||
158 | // "vm_name": []*Service{}, | ||
159 | // } | ||
160 | // | ||
161 | // In JSON format: | ||
162 | // | ||
163 | // { "services": { | ||
164 | // "myVm": [ | ||
165 | // { "id": "myid } | ||
166 | // ] | ||
167 | // } | ||
168 | func (c *ConfigFileNetboxClient) GetServicesForVm(ctx context.Context, vmName string) ([]*Service, error) { | ||
169 | s, ok := c.cfg["services"] | ||
170 | if !ok { | ||
171 | return nil, fmt.Errorf("No key 'services' in config file") | ||
172 | } | ||
173 | |||
174 | services := map[string][]*Service{} | ||
175 | if err := mapstructure.Decode(s, &services); err != nil { | ||
176 | return nil, fmt.Errorf("Key 'services' not in correct format: %w", err) | ||
177 | } | ||
178 | |||
179 | vm, ok := services[vmName] | ||
180 | if !ok { | ||
181 | return nil, fmt.Errorf("VM named %s not in config", vmName) | ||
182 | } | ||
183 | |||
184 | return vm, nil | ||
185 | } | ||
diff --git a/clients/netbox/config_file_client_test.go b/clients/netbox/config_file_client_test.go index bce5165..117db00 100644 --- a/clients/netbox/config_file_client_test.go +++ b/clients/netbox/config_file_client_test.go | |||
@@ -59,8 +59,20 @@ netbox: | |||
59 | tag1: [ "127.0.0.1/8", "10.0.10.0/24" ] | 59 | tag1: [ "127.0.0.1/8", "10.0.10.0/24" ] |
60 | tags: | 60 | tags: |
61 | tag1: [ "127.0.0.1/8", "10.0.10.0/24" ] | 61 | tag1: [ "127.0.0.1/8", "10.0.10.0/24" ] |
62 | services: | ||
63 | testVM: | ||
64 | - id: 1 | ||
65 | name: Test Service | ||
66 | virtual_machine: | ||
67 | id: 2 | ||
68 | name: Test VM | ||
62 | `) | 69 | `) |
63 | 70 | ||
71 | var serviceTestConfig = []byte(`{ | ||
72 | "netbox": { | ||
73 | } | ||
74 | }`) | ||
75 | |||
64 | func init() { | 76 | func init() { |
65 | testFs = fstest.MapFS{} | 77 | testFs = fstest.MapFS{} |
66 | testFs["netbox.foo"] = &fstest.MapFile{Data: jsonTestConfig} | 78 | testFs["netbox.foo"] = &fstest.MapFile{Data: jsonTestConfig} |
@@ -160,6 +172,20 @@ func (s *ConfigFileNetboxClientSuite) TestGetPrefixesWithTagMissingInvalid() { | |||
160 | assert.ErrorContains(s.T(), err, "Key 'tags' not in correct format") | 172 | assert.ErrorContains(s.T(), err, "Key 'tags' not in correct format") |
161 | } | 173 | } |
162 | 174 | ||
175 | func (s *ConfigFileNetboxClientSuite) TestGetServicesForVM() { | ||
176 | svcs, err := s.c.GetServicesForVm(context.TODO(), "testVM") | ||
177 | assert.NoError(s.T(), err) | ||
178 | assert.Equal(s.T(), svcs[0].ID, 1) | ||
179 | assert.Equal(s.T(), svcs[0].Name, "Test Service") | ||
180 | assert.Equal(s.T(), svcs[0].VirtualMachine.ID, 2) | ||
181 | assert.Equal(s.T(), svcs[0].VirtualMachine.Name, "Test VM") | ||
182 | } | ||
183 | |||
184 | func (s *ConfigFileNetboxClientSuite) TestGetServicesForVMMissingVM() { | ||
185 | _, err := s.c.GetServicesForVm(context.TODO(), "foo") | ||
186 | assert.ErrorContains(s.T(), err, "VM named foo not in config") | ||
187 | } | ||
188 | |||
163 | func TestConfigFileNetboxClientSuite(t *testing.T) { | 189 | func TestConfigFileNetboxClientSuite(t *testing.T) { |
164 | suite.Run(t, &ConfigFileNetboxClientSuite{}) | 190 | suite.Run(t, &ConfigFileNetboxClientSuite{}) |
165 | } | 191 | } |
diff --git a/clients/netbox/model.go b/clients/netbox/model.go index 40c4aa6..be4767d 100644 --- a/clients/netbox/model.go +++ b/clients/netbox/model.go | |||
@@ -1,6 +1,10 @@ | |||
1 | package netbox | 1 | package netbox |
2 | 2 | ||
3 | import "fmt" | 3 | import ( |
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | "net" | ||
7 | ) | ||
4 | 8 | ||
5 | type ApiError struct { | 9 | type ApiError struct { |
6 | Status int | 10 | Status int |
@@ -85,3 +89,70 @@ type Prefix struct { | |||
85 | Tags []*Tag `json:"tags"` | 89 | Tags []*Tag `json:"tags"` |
86 | CustomFields map[string]interface{} `json:"custom_fields"` | 90 | CustomFields map[string]interface{} `json:"custom_fields"` |
87 | } | 91 | } |
92 | |||
93 | type ServiceList struct { | ||
94 | Count int `json:"count"` | ||
95 | Next string `json:"next"` | ||
96 | Previous string `json:"previous"` | ||
97 | Results []*Service `json:"results"` | ||
98 | } | ||
99 | |||
100 | type ServiceProtocol struct { | ||
101 | Value string `json:"value"` | ||
102 | Label string `json:"label"` | ||
103 | } | ||
104 | |||
105 | type DeviceOrVM struct { | ||
106 | ID int `json:"id"` | ||
107 | Url string `json:"url"` | ||
108 | Name string `json:"name"` | ||
109 | Display string `json:"display"` | ||
110 | } | ||
111 | |||
112 | type ServiceIPAddress struct { | ||
113 | ID int `json:"id"` | ||
114 | Url string `json:"url"` | ||
115 | Display string `json:"display"` | ||
116 | Family int `json:"family"` | ||
117 | Address net.IP | ||
118 | Network *net.IPNet | ||
119 | } | ||
120 | |||
121 | func (a *ServiceIPAddress) UnmarshalJSON(d []byte) error { | ||
122 | type Alias ServiceIPAddress | ||
123 | v := &struct { | ||
124 | *Alias | ||
125 | Address string `json:"address"` | ||
126 | }{Alias: (*Alias)(a)} | ||
127 | |||
128 | if err := json.Unmarshal(d, v); err != nil { | ||
129 | return err | ||
130 | } | ||
131 | |||
132 | i, n, err := net.ParseCIDR(v.Address) | ||
133 | if err != nil { | ||
134 | return err | ||
135 | } | ||
136 | |||
137 | a.Address = i | ||
138 | a.Network = n | ||
139 | |||
140 | return nil | ||
141 | } | ||
142 | |||
143 | type Service struct { | ||
144 | ID int `json:"id" mapstructure:"id"` | ||
145 | Url string `json:"url" mapstructure:"url"` | ||
146 | Name string `json:"name" mapstructure:"name"` | ||
147 | Display string `json:"display" mapstructure:"display"` | ||
148 | Device *DeviceOrVM `json:"device" mapstructure:"device"` | ||
149 | VirtualMachine *DeviceOrVM `json:"virtual_machine" mapstructure:"virtual_machine"` | ||
150 | Ports []int `json:"ports" mapstructure:"ports"` | ||
151 | Protocol *ServiceProtocol `json:"protocol" mapstructure:"protocol"` | ||
152 | Addresses []*ServiceIPAddress `json:"ipaddresses" mapstructure:"ipaddresses"` | ||
153 | Description string `json:"description" mapstructure:"description"` | ||
154 | Created string `json:"created" mapstructure:"created"` | ||
155 | LastUpdated string `json:"last_updated" mapstructure:"last_updated"` | ||
156 | Tags []*Tag `json:"tags" mapstructure:"tags"` | ||
157 | CustomFields map[string]interface{} `json:"custom_fields" mapstructure:"custom_fields"` | ||
158 | } | ||