Skip to content

Commit 74f7fa5

Browse files
committed
Move common functions in netbox/utils sub-module
This is done in preparation for splitting up the NetBox connector and client to support version specific NetBox clients and to support multiple version of NetBox at the same time. Signed-off-by: Maximilian Wilhelm <[email protected]>
1 parent b92a589 commit 74f7fa5

File tree

3 files changed

+151
-136
lines changed

3 files changed

+151
-136
lines changed

pkg/connector/netbox/netbox.go

+9-135
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ package netbox
1010
import (
1111
"fmt"
1212
"regexp"
13-
"strconv"
1413
"strings"
1514
"sync"
1615
"sync/atomic"
1716
"time"
1817

1918
dbModel "github.com/cloudflare/octopus/pkg/connector/netbox/model"
19+
nbUtils "github.com/cloudflare/octopus/pkg/connector/netbox/utils"
2020
"github.com/cloudflare/octopus/pkg/model"
2121
"github.com/cloudflare/octopus/proto/octopus"
2222

@@ -192,7 +192,7 @@ func (n *NetboxConnector) addDevices(t *model.Topology) error {
192192
topoDev.Role = d.DeviceRole.Slug
193193
topoDev.DeviceType = d.DeviceType.Slug
194194

195-
md, err := metaDataFromTags(d.Tags)
195+
md, err := nbUtils.GetMetaDataFromTags(d.Tags)
196196
if err != nil {
197197
return fmt.Errorf("unable to get meta data: %v", err)
198198
}
@@ -218,7 +218,7 @@ func (n *NetboxConnector) addInterfaces(t *model.Topology) error {
218218
t.DevicesByInterfaceID[nbIfa.ID] = d
219219
t.Interfaces[nbIfa.ID] = ifa
220220

221-
md, err := metaDataFromTags(nbIfa.Tags)
221+
md, err := nbUtils.GetMetaDataFromTags(nbIfa.Tags)
222222
if err != nil {
223223
return fmt.Errorf("unable to get meta data: %v", err)
224224
}
@@ -255,15 +255,15 @@ func (n *NetboxConnector) addInterfaceUnits(t *model.Topology) error {
255255
return fmt.Errorf("can not find interface %s:%s", nbIfa.Device.Name, nbIfa.Parent.Name)
256256
}
257257

258-
vlanTag, err := parseUnitStr(unitStr)
258+
vlanTag, err := nbUtils.ParseUnitStr(unitStr)
259259
if err != nil {
260260
return fmt.Errorf("Unable to convert unit %q (id=%d) (interface %q) to int for %s:%s. Ignoring logical interface.", unitStr, nbIfa.ID, nbIfa.Name, nbIfa.Device.Name, nbIfa.Parent.Name)
261261
}
262262

263263
u := ifa.AddUnitIfNotExists(vlanTag)
264264
t.DevicesByInterfaceID[nbIfa.ID] = d
265265

266-
md, err := metaDataFromTags(nbIfa.Tags)
266+
md, err := nbUtils.GetMetaDataFromTags(nbIfa.Tags)
267267
if err != nil {
268268
return fmt.Errorf("unable to get meta data: %v", err)
269269
}
@@ -274,34 +274,6 @@ func (n *NetboxConnector) addInterfaceUnits(t *model.Topology) error {
274274
return nil
275275
}
276276

277-
func parseUnitStr(unitStr string) (model.VLANTag, error) {
278-
if strings.Contains(unitStr, ".") {
279-
parts := strings.Split(unitStr, ".")
280-
if len(parts) != 2 {
281-
return model.VLANTag{}, fmt.Errorf("invalid unit string %q", unitStr)
282-
}
283-
284-
outerTag, err := strconv.Atoi(parts[0])
285-
if err != nil {
286-
return model.VLANTag{}, fmt.Errorf("unable to convert %q to int: %v", parts[0], err)
287-
}
288-
289-
innerTag, err := strconv.Atoi(parts[1])
290-
if err != nil {
291-
return model.VLANTag{}, fmt.Errorf("unable to convert %q to int: %v", parts[1], err)
292-
}
293-
294-
return model.NewVLANTag(uint16(outerTag), uint16(innerTag)), nil
295-
}
296-
297-
ctag, err := strconv.Atoi(unitStr)
298-
if err != nil {
299-
return model.VLANTag{}, fmt.Errorf("unable to convert %q to int: %v", unitStr, err)
300-
}
301-
302-
return model.NewVLANTag(0, uint16(ctag)), nil
303-
}
304-
305277
func (n *NetboxConnector) addIPAddresses(t *model.Topology) error {
306278
for _, nbIP := range n.ipAddresses {
307279
if nbIP.AssignedObjectID == 0 || nbIP.AssignedObjectTypeID != n.client.GetDcimInterfaceTypeID() {
@@ -323,18 +295,18 @@ func (n *NetboxConnector) addIPAddresses(t *model.Topology) error {
323295
return fmt.Errorf("interface with id %d not found", ifaID)
324296
}
325297

326-
_, vt, err := getInterfaceAndVLANTag(dcimIfa.Name)
298+
_, vt, err := nbUtils.GetInterfaceAndVLANTag(dcimIfa.Name)
327299
if err != nil {
328300
return fmt.Errorf("unable to extract interface name and unit from %q: %v", dcimIfa.Name, err)
329301
}
330302

331-
pfx, err := bnet.PrefixFromString(sanitizeIPAddress(nbIP.Address))
303+
pfx, err := bnet.PrefixFromString(nbUtils.SanitizeIPAddress(nbIP.Address))
332304
if err != nil {
333305
return fmt.Errorf("failed to parse IP %q: %v", nbIP.Address, err)
334306
}
335307

336308
ip := model.NewIP(*pfx)
337-
getCustomFieldData(ip.MetaData, nbIP.CustomFieldData)
309+
nbUtils.GetCustomFieldData(ip.MetaData, nbIP.CustomFieldData)
338310

339311
u := ifa.AddUnitIfNotExists(vt)
340312
if pfx.Addr().IsIPv4() {
@@ -347,26 +319,14 @@ func (n *NetboxConnector) addIPAddresses(t *model.Topology) error {
347319
return nil
348320
}
349321

350-
func sanitizeIPAddress(addr string) string {
351-
if strings.Contains(addr, "/") {
352-
return addr
353-
}
354-
355-
if strings.Contains(addr, ".") {
356-
return addr + "/32"
357-
}
358-
359-
return addr + "/128"
360-
}
361-
362322
func (n *NetboxConnector) addPrefixes(t *model.Topology) error {
363323
for _, p := range n.prefixes {
364324
pfx, err := bnet.PrefixFromString(p.Prefix)
365325
if err != nil {
366326
return fmt.Errorf("failed to parse Prefix %q: %v", p.Prefix, err)
367327
}
368328

369-
md, err := metaDataFromTags(p.Tags)
329+
md, err := nbUtils.GetMetaDataFromTags(p.Tags)
370330
if err != nil {
371331
return fmt.Errorf("failed to get Tags for Prefix %q: %v", p.Prefix, err)
372332
}
@@ -380,36 +340,6 @@ func (n *NetboxConnector) addPrefixes(t *model.Topology) error {
380340
return nil
381341
}
382342

383-
func metaDataFromTags(tags []string) (*model.MetaData, error) {
384-
ret := model.NewMetaData()
385-
for _, tag := range tags {
386-
parts := strings.Split(tag, "=")
387-
388-
// Semantic Tag
389-
if len(parts) == 2 {
390-
if _, exists := ret.SemanticTags[parts[0]]; exists {
391-
return nil, fmt.Errorf("Key %q exists already: %q vs. %q", parts[0], ret.SemanticTags[parts[0]], parts[1])
392-
}
393-
394-
ret.SemanticTags[parts[0]] = parts[1]
395-
396-
} else {
397-
// Regular Tag
398-
ret.Tags = append(ret.Tags, tag)
399-
}
400-
}
401-
402-
return ret, nil
403-
}
404-
405-
func getCustomFieldData(md *model.MetaData, customFieldData string) {
406-
if customFieldData == "" || customFieldData == "{}" {
407-
return
408-
}
409-
410-
md.CustomFieldData = customFieldData
411-
}
412-
413343
func (n *NetboxConnector) getCableEnd(terminationType int32, terminationID int64, t *model.Topology) (*model.CableEnd, error) {
414344
ce := model.CableEnd{}
415345

@@ -573,62 +503,6 @@ func (n *NetboxConnector) addFrontPorts(t *model.Topology) error {
573503
return nil
574504
}
575505

576-
func getInterfaceAndVLANTag(name string) (ifName string, vt model.VLANTag, err error) {
577-
if isLogicalInterface(name) {
578-
return extractInterfaceAndUnit(name)
579-
}
580-
581-
return name, model.VLANTag{}, nil
582-
}
583-
584-
func isLogicalInterface(name string) bool {
585-
return strings.Contains(name, ".")
586-
}
587-
588-
func extractInterfaceAndUnit(name string) (string, model.VLANTag, error) {
589-
extractedVars := reSubMatchMap(ifNameTagsRegexp, name)
590-
if _, exists := extractedVars["intf_name"]; !exists {
591-
return "", model.VLANTag{}, fmt.Errorf("unable to extract interface name from %q", name)
592-
}
593-
594-
if _, exists := extractedVars["inner_tag"]; !exists {
595-
return "", model.VLANTag{}, fmt.Errorf("unable to extract inner tag from %q", name)
596-
}
597-
598-
innerTag, err := strconv.Atoi(extractedVars["inner_tag"])
599-
if err != nil {
600-
return "", model.VLANTag{}, fmt.Errorf("unable to convert inner tag from string %q to int (%q)", extractedVars["inner_tag"], name)
601-
}
602-
603-
vt := model.VLANTag{
604-
InnerTag: uint16(innerTag),
605-
}
606-
607-
outerTagStr := extractedVars["outer_tag"]
608-
if outerTagStr != "" {
609-
outerTag, err := strconv.Atoi(outerTagStr)
610-
if err != nil {
611-
return "", model.VLANTag{}, fmt.Errorf("unable to convert outer tag from string %q to int (%q)", outerTagStr, name)
612-
}
613-
614-
vt.OuterTag = uint16(outerTag)
615-
}
616-
617-
return extractedVars["intf_name"], vt, nil
618-
}
619-
620-
func reSubMatchMap(r *regexp.Regexp, str string) map[string]string {
621-
match := r.FindStringSubmatch(str)
622-
subMatchMap := make(map[string]string)
623-
for i, name := range r.SubexpNames() {
624-
if i != 0 && name != "" && i <= len(match) {
625-
subMatchMap[name] = match[i]
626-
}
627-
}
628-
629-
return subMatchMap
630-
}
631-
632506
func (n *NetboxConnector) StartRefreshRoutine() {
633507
go n.refreshRoutine()
634508
}

pkg/connector/netbox/netbox_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
bnet "github.com/bio-routing/bio-rd/net"
1818
dbModel "github.com/cloudflare/octopus/pkg/connector/netbox/model"
19+
nbUtils "github.com/cloudflare/octopus/pkg/connector/netbox/utils"
1920
octopuspb "github.com/cloudflare/octopus/proto/octopus"
2021
)
2122

@@ -1051,7 +1052,7 @@ func TestExtractInterfaceAndUnit(t *testing.T) {
10511052

10521053
for _, test := range tests {
10531054
t.Run(test.name, func(t *testing.T) {
1054-
ifName, vt, err := extractInterfaceAndUnit(test.input)
1055+
ifName, vt, err := nbUtils.ExtractInterfaceAndUnit(test.input)
10551056
if err != nil {
10561057
if test.wantFail {
10571058
return

pkg/connector/netbox/utils/utils.go

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package utils
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/cloudflare/octopus/pkg/model"
10+
)
11+
12+
var (
13+
ifNameTagsRegexp = regexp.MustCompile(`^(?P<intf_name>.+?)\.((?P<outer_tag>\d+)\.)?(?P<inner_tag>\d+)$`)
14+
)
15+
16+
func ParseUnitStr(unitStr string) (model.VLANTag, error) {
17+
if strings.Contains(unitStr, ".") {
18+
parts := strings.Split(unitStr, ".")
19+
if len(parts) != 2 {
20+
return model.VLANTag{}, fmt.Errorf("invalid unit string %q", unitStr)
21+
}
22+
23+
outerTag, err := strconv.Atoi(parts[0])
24+
if err != nil {
25+
return model.VLANTag{}, fmt.Errorf("unable to convert %q to int: %v", parts[0], err)
26+
}
27+
28+
innerTag, err := strconv.Atoi(parts[1])
29+
if err != nil {
30+
return model.VLANTag{}, fmt.Errorf("unable to convert %q to int: %v", parts[1], err)
31+
}
32+
33+
return model.NewVLANTag(uint16(outerTag), uint16(innerTag)), nil
34+
}
35+
36+
ctag, err := strconv.Atoi(unitStr)
37+
if err != nil {
38+
return model.VLANTag{}, fmt.Errorf("unable to convert %q to int: %v", unitStr, err)
39+
}
40+
41+
return model.NewVLANTag(0, uint16(ctag)), nil
42+
}
43+
44+
func SanitizeIPAddress(addr string) string {
45+
if strings.Contains(addr, "/") {
46+
return addr
47+
}
48+
49+
if strings.Contains(addr, ".") {
50+
return addr + "/32"
51+
}
52+
53+
return addr + "/128"
54+
}
55+
56+
func GetMetaDataFromTags(tags []string) (*model.MetaData, error) {
57+
ret := model.NewMetaData()
58+
for _, tag := range tags {
59+
parts := strings.Split(tag, "=")
60+
61+
// Semantic Tag
62+
if len(parts) == 2 {
63+
if _, exists := ret.SemanticTags[parts[0]]; exists {
64+
return nil, fmt.Errorf("key %q exists already: %q vs. %q", parts[0], ret.SemanticTags[parts[0]], parts[1])
65+
}
66+
67+
ret.SemanticTags[parts[0]] = parts[1]
68+
69+
} else {
70+
// Regular Tag
71+
ret.Tags = append(ret.Tags, tag)
72+
}
73+
}
74+
75+
return ret, nil
76+
}
77+
78+
func GetCustomFieldData(md *model.MetaData, customFieldData string) {
79+
if customFieldData == "" || customFieldData == "{}" {
80+
return
81+
}
82+
83+
md.CustomFieldData = customFieldData
84+
}
85+
86+
func GetInterfaceAndVLANTag(name string) (ifName string, vt model.VLANTag, err error) {
87+
if isLogicalInterface(name) {
88+
return ExtractInterfaceAndUnit(name)
89+
}
90+
91+
return name, model.VLANTag{}, nil
92+
}
93+
94+
func isLogicalInterface(name string) bool {
95+
return strings.Contains(name, ".")
96+
}
97+
98+
func ExtractInterfaceAndUnit(name string) (string, model.VLANTag, error) {
99+
extractedVars := reSubMatchMap(ifNameTagsRegexp, name)
100+
if _, exists := extractedVars["intf_name"]; !exists {
101+
return "", model.VLANTag{}, fmt.Errorf("unable to extract interface name from %q", name)
102+
}
103+
104+
if _, exists := extractedVars["inner_tag"]; !exists {
105+
return "", model.VLANTag{}, fmt.Errorf("unable to extract inner tag from %q", name)
106+
}
107+
108+
innerTag, err := strconv.Atoi(extractedVars["inner_tag"])
109+
if err != nil {
110+
return "", model.VLANTag{}, fmt.Errorf("unable to convert inner tag from string %q to int (%q)", extractedVars["inner_tag"], name)
111+
}
112+
113+
vt := model.VLANTag{
114+
InnerTag: uint16(innerTag),
115+
}
116+
117+
outerTagStr := extractedVars["outer_tag"]
118+
if outerTagStr != "" {
119+
outerTag, err := strconv.Atoi(outerTagStr)
120+
if err != nil {
121+
return "", model.VLANTag{}, fmt.Errorf("unable to convert outer tag from string %q to int (%q)", outerTagStr, name)
122+
}
123+
124+
vt.OuterTag = uint16(outerTag)
125+
}
126+
127+
return extractedVars["intf_name"], vt, nil
128+
}
129+
130+
func reSubMatchMap(r *regexp.Regexp, str string) map[string]string {
131+
match := r.FindStringSubmatch(str)
132+
subMatchMap := make(map[string]string)
133+
for i, name := range r.SubexpNames() {
134+
if i != 0 && name != "" && i <= len(match) {
135+
subMatchMap[name] = match[i]
136+
}
137+
}
138+
139+
return subMatchMap
140+
}

0 commit comments

Comments
 (0)