aboutsummaryrefslogtreecommitdiff
path: root/clients
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2023-08-01 17:39:13 -0700
committerMike Crute <mike@crute.us>2023-08-01 17:39:13 -0700
commit7fbdc96ad276b7a24fca428e42578494c0dcc35b (patch)
tree1b5521e5eba6a2c6baa090f193fa1d116762763c /clients
parent058e4faba9c1531661234a5adaa7a6c3791f92d4 (diff)
downloadgolib-7fbdc96ad276b7a24fca428e42578494c0dcc35b.tar.bz2
golib-7fbdc96ad276b7a24fca428e42578494c0dcc35b.tar.xz
golib-7fbdc96ad276b7a24fca428e42578494c0dcc35b.zip
netbox: add GetServicesForVm callclients/netbox/v3.3.0
Diffstat (limited to 'clients')
-rw-r--r--clients/netbox/client.go30
-rw-r--r--clients/netbox/config_file_client.go68
-rw-r--r--clients/netbox/config_file_client_test.go26
-rw-r--r--clients/netbox/model.go73
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 (
14type NetboxClient interface { 14type 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
19type BasicNetboxClient struct { 20type 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
162func (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// }
85func (c *ConfigFileNetboxClient) GetSitePrefixesWithTag(ctx context.Context, site string, tag string) ([]*net.IPNet, error) { 85func (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// }
168func (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
71var serviceTestConfig = []byte(`{
72 "netbox": {
73 }
74}`)
75
64func init() { 76func 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
175func (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
184func (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
163func TestConfigFileNetboxClientSuite(t *testing.T) { 189func 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 @@
1package netbox 1package netbox
2 2
3import "fmt" 3import (
4 "encoding/json"
5 "fmt"
6 "net"
7)
4 8
5type ApiError struct { 9type 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
93type ServiceList struct {
94 Count int `json:"count"`
95 Next string `json:"next"`
96 Previous string `json:"previous"`
97 Results []*Service `json:"results"`
98}
99
100type ServiceProtocol struct {
101 Value string `json:"value"`
102 Label string `json:"label"`
103}
104
105type DeviceOrVM struct {
106 ID int `json:"id"`
107 Url string `json:"url"`
108 Name string `json:"name"`
109 Display string `json:"display"`
110}
111
112type 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
121func (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
143type 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}