Skip to content

Commit

Permalink
Merge pull request #36 from cpaillet/snmp
Browse files Browse the repository at this point in the history
feat(api): add SNMP configuration
  • Loading branch information
cpaillet authored Jul 1, 2024
2 parents 56158c8 + 5e966ca commit 45d4207
Show file tree
Hide file tree
Showing 16 changed files with 12,160 additions and 17 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ run:
skip-files:
- internal/model/openconfig/oc_path.go
- internal/model/openconfig/oc.go
- internal/model/ietf/ietf.go

# Invariable parameters #

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ run:
-X 'github.com/criteo/data-aggregation-api/internal/version.buildTime=$(BUILDDATE)'" \
./cmd/data-aggregation-api

update_openconfig:
./update_openconfig.sh
update_yang_models:
./update_yang_models.sh
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ You need to update the following part in the code:
4. add your convertor in `internal/convertors/...`

3. execute your convertor (`internal/convertors/device/device.go`):
- GenerateOpenconfig()
- Generateconfigs()
50 changes: 50 additions & 0 deletions internal/api/router/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,56 @@ func (m *Manager) getDeviceOpenConfig(w http.ResponseWriter, _ *http.Request, ps
_, _ = w.Write(cfg)
}

// getDeviceIETFConfig endpoint returns Ietf JSON for one or all devices.
func (m *Manager) getDeviceIETFConfig(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) {
w.Header().Set(contentType, applicationJSON)
hostname := ps.ByName(hostnameKey)
if ps.ByName(hostnameKey) == wildcard {
cfg, err := m.devices.GetAllDevicesIETFConfigJSON()
if err != nil {
log.Error().Err(err).Send()
w.WriteHeader(http.StatusInternalServerError)
return
}

_, _ = w.Write(cfg)
return
}

cfg, err := m.devices.GetDeviceIETFConfigJSON(hostname)
if err != nil {
log.Error().Err(err).Send()
w.WriteHeader(http.StatusInternalServerError)
return
}
_, _ = w.Write(cfg)
}

// getDeviceConfig endpoint returns Ietf & openconfig JSON for one or all devices.
func (m *Manager) getDeviceConfig(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) {
w.Header().Set(contentType, applicationJSON)
hostname := ps.ByName(hostnameKey)
if ps.ByName(hostnameKey) == wildcard {
cfg, err := m.devices.GetAllDevicesConfigJSON()
if err != nil {
log.Error().Err(err).Send()
w.WriteHeader(http.StatusInternalServerError)
return
}

_, _ = w.Write(cfg)
return
}

cfg, err := m.devices.GetDeviceConfigJSON(hostname)
if err != nil {
log.Error().Err(err).Send()
w.WriteHeader(http.StatusInternalServerError)
return
}
_, _ = w.Write(cfg)
}

// getLastReport returns the last or current report.
func (m *Manager) getLastReport(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
out, err := m.reports.GetLastJSON()
Expand Down
6 changes: 6 additions & 0 deletions internal/api/router/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ type DevicesRepository interface {
IsAFKEnabledJSON(hostname string) ([]byte, error)
GetAllDevicesOpenConfigJSON() ([]byte, error)
GetDeviceOpenConfigJSON(hostname string) ([]byte, error)
GetAllDevicesIETFConfigJSON() ([]byte, error)
GetDeviceIETFConfigJSON(hostname string) ([]byte, error)
GetAllDevicesConfigJSON() ([]byte, error)
GetDeviceConfigJSON(hostname string) ([]byte, error)
}

type Manager struct {
Expand Down Expand Up @@ -56,6 +60,8 @@ func (m *Manager) ListenAndServe(ctx context.Context, address string, port int)
router.GET("/api/health", healthCheck)
router.GET("/v1/devices/:hostname/afk_enabled", withAuth.Wrap(m.getAFKEnabled))
router.GET("/v1/devices/:hostname/openconfig", withAuth.Wrap(m.getDeviceOpenConfig))
router.GET("/v1/devices/:hostname/ietfconfig", withAuth.Wrap(m.getDeviceIETFConfig))
router.GET("/v1/devices/:hostname/config", withAuth.Wrap(m.getDeviceConfig))
router.GET("/v1/report/last", withAuth.Wrap(m.getLastReport))
router.GET("/v1/report/last/complete", withAuth.Wrap(m.getLastCompleteReport))
router.GET("/v1/report/last/successful", withAuth.Wrap(m.getLastSuccessfulReport))
Expand Down
65 changes: 56 additions & 9 deletions internal/convertor/device/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import (

bgpconvertors "github.com/criteo/data-aggregation-api/internal/convertor/bgp"
rpconvertors "github.com/criteo/data-aggregation-api/internal/convertor/routingpolicy"
snmpconvertors "github.com/criteo/data-aggregation-api/internal/convertor/snmp"
"github.com/criteo/data-aggregation-api/internal/ingestor/repository"
"github.com/criteo/data-aggregation-api/internal/model/cmdb/bgp"
"github.com/criteo/data-aggregation-api/internal/model/cmdb/routingpolicy"
"github.com/criteo/data-aggregation-api/internal/model/cmdb/snmp"
"github.com/criteo/data-aggregation-api/internal/model/dcim"
"github.com/criteo/data-aggregation-api/internal/model/ietf"
"github.com/criteo/data-aggregation-api/internal/model/openconfig"
"github.com/openconfig/ygot/ygot"
"github.com/rs/zerolog/log"
Expand All @@ -22,15 +25,18 @@ const AFKEnabledTag = "afk-enabled"
var defaultInstance = "default"

type GeneratedConfig struct {
Openconfig *openconfig.Device
JSON string
IETF *ietf.Device
JSONIETF string
Openconfig *openconfig.Device
JSONOpenConfig string
}

type Device struct {
mutex *sync.Mutex
Dcim *dcim.NetworkDevice
Config *GeneratedConfig
BGPGlobalConfig *bgp.BGPGlobal
SNMP *snmp.SNMP
Sessions []*bgp.Session
PeerGroups []*bgp.PeerGroup
PrefixLists []*routingpolicy.PrefixList
Expand Down Expand Up @@ -92,12 +98,16 @@ func NewDevice(dcimInfo *dcim.NetworkDevice, devicesData *repository.AssetsPerDe
return nil, fmt.Errorf("no route-policies found for %s", dcimInfo.Hostname)
}

device.SNMP, ok = devicesData.SNMP[dcimInfo.Hostname]
if !ok {
log.Warn().Msgf("no snmp found for %s", dcimInfo.Hostname)
}
return device, nil
}

// GenerateOpenconfig generate the OpenConfig data for the current device.
// Generateconfigs generate the Config (openconfig & ietf) data for the current device.
// The CMDB data must have been precomputed before running this method.
func (d *Device) GenerateOpenconfig() error {
func (d *Device) Generateconfigs() error {
d.mutex.Lock()
defer d.mutex.Unlock()

Expand Down Expand Up @@ -139,23 +149,60 @@ func (d *Device) GenerateOpenconfig() error {
Indent: " ",
},
)

if err != nil {
return fmt.Errorf("failed to transform an openconfig device specification (%s) into JSON using ygot: %w", d.Dcim.Hostname, err)
}

d.Config = &GeneratedConfig{
Openconfig: &config,
JSON: devJSON,
Openconfig: &config,
JSONOpenConfig: devJSON,
IETF: nil,
JSONIETF: "{}",
}

if d.SNMP == nil {
log.Warn().Msgf("%s don't have a Snmp configuration, skip IetfConfig", d.Dcim.Hostname)
} else {
IetfSystem := snmpconvertors.SNMPtoIETFfSystem(d.SNMP)
IetfSnmp := snmpconvertors.SNMPtoIETFsnmp(d.SNMP)

d.Config.IETF = &ietf.Device{
System: &IetfSystem,
Snmp: &IetfSnmp,
}

d.Config.JSONIETF, err = ygot.EmitJSON(
d.Config.IETF,
&ygot.EmitJSONConfig{
Format: ygot.RFC7951,
SkipValidation: false,
Indent: " ",
},
)
if err != nil {
return fmt.Errorf("failed to transform an ietf device specification (%s) into JSON using ygot: %w", d.Dcim.Hostname, err)
}
}
return nil
}

// GetCompactJSON returns OpenConfig result in not indented JSON format.
// GetCompactOpenconfigJSON returns OpenConfig result in not indented JSON format.
// Generated JSON is already indented by Ygot - currently there is no option to not indent the JSON.
func (d *Device) GetCompactJSON() ([]byte, error) {
func (d *Device) GetCompactOpenconfigJSON() ([]byte, error) {
out := bytes.NewBuffer(nil)
err := json.Compact(out, []byte(d.Config.JSONOpenConfig))
if err != nil {
return nil, err
}
return out.Bytes(), nil
}

// GetCompactIETFJSON returns IETF result in not indented JSON format.
// GetCompactIETFJSON JSON is already indented by Ygot - currently there is no option to not indent the JSON.
func (d *Device) GetCompactIETFJSON() ([]byte, error) {
out := bytes.NewBuffer(nil)
err := json.Compact(out, []byte(d.Config.JSON))
err := json.Compact(out, []byte(d.Config.JSONIETF))
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 45d4207

Please sign in to comment.