Skip to content

Commit

Permalink
5 add tag to all objects created or updated by the script (#6)
Browse files Browse the repository at this point in the history
* #5 get or create tag

* #5 add tag with update

* #5 only add tag when needed
  • Loading branch information
mattieserver authored Sep 20, 2024
1 parent dcb59ce commit b3ea1e9
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 35 deletions.
8 changes: 5 additions & 3 deletions cmd/netbox-oxidized-sync/netbox-oxidized-sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

func worker(id int, jobs <-chan httphelper.OxidizedNode, results chan<- int, netboxdevices *[]model.NetboxDevice, oxidizedhttp *httphelper.OxidizedHTTPClient, netboxhttp *httphelper.NetboxHTTPClient) {
for j := range jobs {
log.Printf("Got oxided device: '%s' on worker %s",j.Name, strconv.Itoa(id), )
log.Printf("Got oxided device: '%s' on worker %s", j.Name, strconv.Itoa(id))

idx := slices.IndexFunc(*netboxdevices, func(c model.NetboxDevice) bool { return c.Name == j.Name })
if idx == -1 {
Expand All @@ -37,7 +37,7 @@ func worker(id int, jobs <-chan httphelper.OxidizedNode, results chan<- int, net
}
interfacesToUpdate := netboxparser.ParseFortigateInterfaces(fortigateInterfaces, &netboxInterfaceForDevice, strconv.Itoa(netboxDevice.ID))
netboxhttp.UpdateOrCreateInferface(&interfacesToUpdate, &netboxVlansForSite, netboxDevice.Site.ID, netboxDevice.Tenant.ID)

default:
log.Printf("Model '%s' currently not supported", j.Model)
}
Expand All @@ -63,7 +63,7 @@ func loadOxidizedDevices(oxidizedhttp *httphelper.OxidizedHTTPClient, netboxhttp
go worker(w, jobs, results, &devices, oxidizedhttp, netboxhttp)
}

for _, element := range nodes {
for _, element := range nodes {
jobs <- element
}
close(jobs)
Expand All @@ -84,5 +84,7 @@ func main() {
netboxhttp := httphelper.NewNetbox(conf.Netbox.BaseURL, conf.Netbox.APIKey, conf.Netbox.Roles)
oxidizedhttp := httphelper.NewOxidized(conf.Oxidized.BaseURL, conf.Oxidized.Username, conf.Oxidized.Password)

netboxhttp.GetManagedTag(conf.Netbox.TagName)

loadOxidizedDevices(&oxidizedhttp, &netboxhttp)
}
3 changes: 2 additions & 1 deletion configs/example.settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"netbox": {
"base_url": "http://localhost:8000",
"api_key": "xxxx-xxxx-xxxx",
"roles": ""
"roles": "",
"tag-name": "oxidized-sync"
},
"oxidized": {
"base_url": "http://localhost:8001",
Expand Down
1 change: 1 addition & 0 deletions internal/confighelper/confighelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Config struct {
BaseURL string `json:"base_url"`
APIKey string `json:"api_key"`
Roles string `json:"roles"`
TagName string `json:"tag-name"`
} `json:"netbox"`
Oxidized struct {
BaseURL string `json:"base_url"`
Expand Down
135 changes: 111 additions & 24 deletions internal/httphelper/netbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,45 +20,56 @@ type netboxResult struct {
}

type interfacePatchData struct {
Description string `json:"description,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
Parent int `json:"parent,omitempty"`
Lag int `json:"lag,omitempty"`
Bridge int `json:"bridge,omitempty"`
InterfaceType string `json:"type,omitempty"`
Mode string `json:"mode,omitempty"`
UntaggedVlan int `json:"untagged_vlan,omitempty"`
Description string `json:"description,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
Parent int `json:"parent,omitempty"`
Lag int `json:"lag,omitempty"`
Bridge int `json:"bridge,omitempty"`
InterfaceType string `json:"type,omitempty"`
Mode string `json:"mode,omitempty"`
UntaggedVlan int `json:"untagged_vlan,omitempty"`
Tags []string `json:"tags,omitempty"`
}

type interfacePostData struct {
Device int `json:"device"`
Name string `json:"name"`
InterfaceType string `json:"type"`
Description string `json:"description,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
UntaggedVlan int `json:"untagged_vlan,omitempty"`
Mode string `json:"mode,omitempty"`
Parent int `json:"parent,omitempty"`
Bridge int `json:"bridge,omitempty"`
Lag int `json:"lag,omitempty"`
Device int `json:"device"`
Name string `json:"name"`
InterfaceType string `json:"type"`
Description string `json:"description,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
UntaggedVlan int `json:"untagged_vlan,omitempty"`
Mode string `json:"mode,omitempty"`
Parent int `json:"parent,omitempty"`
Bridge int `json:"bridge,omitempty"`
Lag int `json:"lag,omitempty"`
Tags []string `json:"tags,omitempty"`
}

type vlanPostData struct {
SiteId int `json:"site,omitempty"`
TenantId int `json:"tenant,omitempty"`
VlanId int `json:"vid"`
Name string `json:"name"`
SiteId int `json:"site,omitempty"`
TenantId int `json:"tenant,omitempty"`
VlanId int `json:"vid"`
Name string `json:"name"`
Tags []string `json:"tags,omitempty"`
}

type tagPostData struct {
Name string `json:"name"`
Slug string `json:"slug"`
Color string `json:"color,omitempty"`
Description string `json:"description,omitempty"`
}

type netboxData interface {
model.NetboxInterface | model.NetboxDevice | model.NetboxVlan
model.NetboxInterface | model.NetboxDevice | model.NetboxVlan | model.NetboxTag
}

type NetboxHTTPClient struct {
apikey string
baseurl string
client http.Client
rolesfilter string
defaultTag model.NetboxTag
}

func NewNetbox(baseurl string, apikey string, roles string) NetboxHTTPClient {
Expand All @@ -80,10 +91,68 @@ func NewNetbox(baseurl string, apikey string, roles string) NetboxHTTPClient {
rolesfilter = sb.String()
}

e := NetboxHTTPClient{apikey, baseurl, *client, rolesfilter}
e := NetboxHTTPClient{apikey, baseurl, *client, rolesfilter, model.NetboxTag{}}
return e
}

func (e *NetboxHTTPClient) GetManagedTag(tagName string) {
tag, err := getNetboxTagByName(tagName, e)
if err != nil {
slog.Error("Error getting tags", err)
}
if tag.ID == 0 {
newTag := e.createNetboxTag(tagName)
e.defaultTag = newTag
} else {
e.defaultTag = tag
}
}

func getNetboxTagByName(tagName string, e *NetboxHTTPClient) (model.NetboxTag, error) {
requestURL := fmt.Sprintf("%s/api/extras/tags/", e.baseurl)
tags, err := apiRequest[model.NetboxTag](requestURL, e)
if err != nil {
return model.NetboxTag{}, err
}
for _, iface := range tags {
if iface.Name == tagName {
return iface, nil
}
}
return model.NetboxTag{}, nil
}

func slugify(input string) string {
var result string

result = strings.ToLower(input)
result = strings.Replace(result, " ", "-", -1)

return result
}

func (e *NetboxHTTPClient) createNetboxTag(tagName string) model.NetboxTag {
var postData tagPostData
postData.Name = tagName
postData.Slug = slugify(tagName)
postData.Description = "Auto generated tag to track objects created by the oxidized sync"
postData.Color = "72599f"

data, _ := json.Marshal(postData)
requestURL := fmt.Sprintf("%s/api/extras/tags/", e.baseurl)
resBody, err := TokenAuthHTTPPost(requestURL, e.apikey, &e.client, data)
if err != nil {
slog.Error(err.Error())
}

var result model.NetboxTag
err = json.Unmarshal(resBody, &result)
if err != nil {
slog.Error(err.Error())
}
return result
}

func loopAPIRequest(path string, e *NetboxHTTPClient) (netboxResult, error) {
resBody, err := TokenAuthHTTPGet(path, e.apikey, &e.client)
if err != nil {
Expand Down Expand Up @@ -165,6 +234,7 @@ func (e *NetboxHTTPClient) createVlan(SiteId int, TenantId int, VlanId int, Name
postData.SiteId = SiteId
postData.VlanId = VlanId
postData.TenantId = TenantId
postData.Tags = []string{strconv.Itoa(e.defaultTag.ID)}

data, _ := json.Marshal(postData)
requestURL := fmt.Sprintf("%s/api/ipam/vlans/", e.baseurl)
Expand Down Expand Up @@ -232,6 +302,10 @@ func (e *NetboxHTTPClient) updateInterface(port model.NetboxInterfaceUpdateCreat
if port.PortTypeUpdate == "virtual" {
patchData.InterfaceType = "virtual"
}
if port.PortTypeUpdate == "bridge" {
patchData.InterfaceType = "bridge"
}

}
if port.VlanMode != "" {
patchData.Mode = port.VlanMode
Expand All @@ -248,6 +322,17 @@ func (e *NetboxHTTPClient) updateInterface(port model.NetboxInterfaceUpdateCreat
}
}

if len(port.Tags) != 0 {
for _,tag := range port.Tags {
if tag != strconv.Itoa(e.defaultTag.ID) {
patchData.Tags = append(patchData.Tags, tag)
}
}
patchData.Tags = append(patchData.Tags, strconv.Itoa(e.defaultTag.ID))
} else {
patchData.Tags = []string{strconv.Itoa(e.defaultTag.ID)}
}

data, _ := json.Marshal(patchData)
requestURL := fmt.Sprintf("%s/%s%s/", e.baseurl, "api/dcim/interfaces/", port.InterfaceId)
_, err := TokenAuthHTTPPatch(requestURL, e.apikey, &e.client, data)
Expand Down Expand Up @@ -331,6 +416,8 @@ func (e *NetboxHTTPClient) createInterface(port model.NetboxInterfaceUpdateCreat
}
}

postData.Tags = []string{strconv.Itoa(e.defaultTag.ID)}

data, _ := json.Marshal(postData)
requestURL := fmt.Sprintf("%s/%s", e.baseurl, "api/dcim/interfaces/")
_, err := TokenAuthHTTPPost(requestURL, e.apikey, &e.client, data)
Expand Down
32 changes: 29 additions & 3 deletions internal/model/DeviceInterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ type NetboxInterface struct {
Label string `json:"label"`
} `json:"type"`
Enabled bool `json:"enabled"`
Parent interface{} `json:"parent"`
Parent struct{
ID int `json:"id"`
Name string `json:"name"`
} `json:"parent"`
Bridge struct {
ID int `json:"id"`
Name string `json:"name"`
Expand Down Expand Up @@ -91,8 +94,15 @@ type NetboxInterface struct {
ConnectedEndpoints interface{} `json:"connected_endpoints"`
ConnectedEndpointsType interface{} `json:"connected_endpoints_type"`
ConnectedEndpointsReachable interface{} `json:"connected_endpoints_reachable"`
Tags []interface{} `json:"tags"`
CustomFields struct {
Tags []struct {
ID int `json:"id"`
URL string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Slug string `json:"slug"`
Color string `json:"color"`
} `json:"tags"`
CustomFields struct {
} `json:"custom_fields"`
Created time.Time `json:"created"`
LastUpdated time.Time `json:"last_updated"`
Expand Down Expand Up @@ -221,6 +231,8 @@ type NetboxInterfaceUpdateCreate struct {
VlanMode string
VlanId string
InterfaceId string
Tags []string
Matched bool
}

type NetboxVlan struct {
Expand Down Expand Up @@ -261,3 +273,17 @@ type NetboxVlan struct {
LastUpdated time.Time `json:"last_updated"`
PrefixCount int `json:"prefix_count"`
}

type NetboxTag struct {
ID int `json:"id"`
URL string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Slug string `json:"slug"`
Color string `json:"color"`
Description string `json:"description"`
ObjectTypes []string `json:"object_types"`
TaggedItems int `json:"tagged_items"`
Created time.Time `json:"created"`
LastUpdated time.Time `json:"last_updated"`
}
19 changes: 15 additions & 4 deletions internal/netboxparser/fortitonetbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ func processPort(port model.FortigateInterface, allMembers map[string]int, forti
Name: port.Name,
PortType: port.InterfaceType,
InterfaceId: strconv.Itoa(netboxInterface.ID),
Matched : true,
}

if len(netboxInterface.Tags) != 0 {
for _, tag := range netboxInterface.Tags {
matched.Tags = append(matched.Tags, strconv.Itoa(tag.ID))
}
}

if port.InterfaceType == lagName && netboxInterface.Type.Value != "lag" {
Expand Down Expand Up @@ -72,8 +79,10 @@ func processPort(port model.FortigateInterface, allMembers map[string]int, forti
}
if port.InterfaceType == "vlan" {
if port.Parent != "" {
matched.Parent = port.Parent
matched.ParentId = getParentID(matched.Parent, netboxDeviceInterfaces)
matched.ParentId = getParentID(port.Parent, netboxDeviceInterfaces)
if matched.ParentId != strconv.Itoa(netboxInterface.Parent.ID) {
matched.Parent = port.Parent
}
}

if netboxInterface.Mode.Value != "access" {
Expand All @@ -97,7 +106,7 @@ func processPort(port model.FortigateInterface, allMembers map[string]int, forti
}
}

if matched == (model.NetboxInterfaceUpdateCreate{}) {
if !matched.Matched {
if port.InterfaceType == "physical" && port.Name != "modem" && !strings.HasPrefix(port.Name, "npu") {
matched.Mode = "create"
matched.Name = port.Name
Expand Down Expand Up @@ -146,7 +155,9 @@ func processPort(port model.FortigateInterface, allMembers map[string]int, forti
}
} else {
if matched.Description != "" || matched.Status != "" || matched.PortTypeUpdate != "" || matched.Parent != "" || matched.VlanMode != "" {
matched.Mode = "update"
if !strings.HasPrefix(port.Parent, "npu") {
matched.Mode = "update"
}
}
}
return matched
Expand Down

0 comments on commit b3ea1e9

Please sign in to comment.