@@ -33,6 +33,7 @@ type MerakiClient struct {
33
33
auth runtime.ClientAuthInfoWriter
34
34
orgs []orgDesc
35
35
timeout time.Duration
36
+ cache * clientCache
36
37
}
37
38
38
39
type orgDesc struct {
@@ -50,8 +51,9 @@ type networkDesc struct {
50
51
}
51
52
52
53
const (
53
- ControllerKey = "meraki_controller_name"
54
- MerakiApiKey = "KENTIK_MERAKI_API_KEY"
54
+ ControllerKey = "meraki_controller_name"
55
+ MerakiApiKey = "KENTIK_MERAKI_API_KEY"
56
+ DeviceCacheDuration = time .Duration (24 ) * time .Hour
55
57
)
56
58
57
59
func NewMerakiClient (jchfChan chan []* kt.JCHF , gconf * kt.SnmpGlobalConfig , conf * kt.SnmpDeviceConfig , metrics * kt.SnmpDeviceMetric , log logger.ContextL ) (* MerakiClient , error ) {
@@ -64,6 +66,7 @@ func NewMerakiClient(jchfChan chan []*kt.JCHF, gconf *kt.SnmpGlobalConfig, conf
64
66
orgs : []orgDesc {},
65
67
auth : httptransport .APIKeyAuth ("X-Cisco-Meraki-API-Key" , "header" , kt .LookupEnvString (MerakiApiKey , conf .Ext .MerakiConfig .ApiKey )),
66
68
timeout : 30 * time .Second ,
69
+ cache : newClientCache (log ),
67
70
}
68
71
69
72
host := conf .Ext .MerakiConfig .Host
@@ -1212,6 +1215,7 @@ type deviceStatusWrapper struct {
1212
1215
org orgDesc
1213
1216
NetworkName string `json:"networkName"`
1214
1217
device * organizations.GetOrganizationDevicesStatusesOKBodyItems0
1218
+ info * organizations.GetOrganizationDevicesOKBodyItems0
1215
1219
}
1216
1220
1217
1221
func (c * MerakiClient ) getDeviceStatus (dur time.Duration ) ([]* kt.JCHF , error ) {
@@ -1278,9 +1282,118 @@ func (c *MerakiClient) getDeviceStatus(dur time.Duration) ([]*kt.JCHF, error) {
1278
1282
return nil , nil
1279
1283
}
1280
1284
1285
+ // Next, get device info to add more info about these devices.
1286
+ err := c .getDeviceInfo (devices )
1287
+ if err != nil {
1288
+ return nil , err
1289
+ }
1290
+
1281
1291
return c .parseDeviceStatus (devices )
1282
1292
}
1283
1293
1294
+ type clientCache struct {
1295
+ log logger.ContextL
1296
+ deviceInfoTime time.Time
1297
+ deviceInfo []* organizations.GetOrganizationDevicesOKBodyItems0
1298
+ }
1299
+
1300
+ func newClientCache (log logger.ContextL ) * clientCache {
1301
+ return & clientCache {
1302
+ log : log ,
1303
+ }
1304
+ }
1305
+
1306
+ func (c * clientCache ) getDeviceInfo () ([]* organizations.GetOrganizationDevicesOKBodyItems0 , bool ) {
1307
+ if c .deviceInfoTime .Add (DeviceCacheDuration ).Before (time .Now ()) { // No information, cache invalid or old.
1308
+ return nil , false
1309
+ }
1310
+
1311
+ return c .deviceInfo , true
1312
+ }
1313
+
1314
+ func (c * clientCache ) setDeviceInfo (infos []* organizations.GetOrganizationDevicesOKBodyItems0 ) {
1315
+ c .deviceInfo = infos
1316
+ c .deviceInfoTime = time .Now ()
1317
+ }
1318
+
1319
+ func (c * MerakiClient ) getDeviceInfo (devices []* deviceStatusWrapper ) error {
1320
+ // Build up a map of product types to filter on.
1321
+ productTypes := map [string ]bool {}
1322
+ for _ , pt := range c .conf .Ext .MerakiConfig .ProductTypes {
1323
+ productTypes [pt ] = true
1324
+ }
1325
+
1326
+ var getDeviceInfo func (nextToken string , org orgDesc , devices * []* deviceStatusWrapper , cache * []* organizations.GetOrganizationDevicesOKBodyItems0 ) error
1327
+ getDeviceInfo = func (nextToken string , org orgDesc , devices * []* deviceStatusWrapper , cache * []* organizations.GetOrganizationDevicesOKBodyItems0 ) error {
1328
+ params := organizations .NewGetOrganizationDevicesParamsWithTimeout (c .timeout )
1329
+ params .SetOrganizationID (org .ID )
1330
+ if nextToken != "" {
1331
+ params .SetStartingAfter (& nextToken )
1332
+ }
1333
+
1334
+ prod , err := c .client .Organizations .GetOrganizationDevices (params , c .auth )
1335
+ if err != nil {
1336
+ return err
1337
+ }
1338
+
1339
+ // Store these for some tail recursion.
1340
+ raw := prod .GetPayload ()
1341
+
1342
+ for _ , device := range raw {
1343
+ // Filter for networks here.
1344
+ if _ , ok := org .networks [device .NetworkID ]; ! ok {
1345
+ continue
1346
+ }
1347
+ // Also filter on product types.
1348
+ if len (productTypes ) > 0 && ! productTypes [device .ProductType ] {
1349
+ continue
1350
+ }
1351
+
1352
+ ld := device
1353
+ * cache = append (* cache , ld )
1354
+ for _ , wrap := range * devices {
1355
+ if wrap .device .Serial == device .Serial {
1356
+ wrap .info = ld
1357
+ }
1358
+ }
1359
+ }
1360
+
1361
+ // Recursion!
1362
+ nextLink := getNextLink (prod .Link )
1363
+ if nextLink != "" {
1364
+ return getDeviceInfo (nextLink , org , devices , cache )
1365
+ } else {
1366
+ return nil
1367
+ }
1368
+ }
1369
+
1370
+ // First check the cache.
1371
+ if cc , ok := c .cache .getDeviceInfo (); ok {
1372
+ // We have a valid cache, don't use rest of call.
1373
+ for _ , device := range cc {
1374
+ for _ , wrap := range devices {
1375
+ if wrap .device .Serial == device .Serial {
1376
+ ld := device
1377
+ wrap .info = ld
1378
+ }
1379
+ }
1380
+ }
1381
+ return nil
1382
+ }
1383
+
1384
+ // Need to build up cache case here.
1385
+ cache := []* organizations.GetOrganizationDevicesOKBodyItems0 {}
1386
+ for _ , org := range c .orgs {
1387
+ err := getDeviceInfo ("" , org , & devices , & cache )
1388
+ if err != nil {
1389
+ return err
1390
+ }
1391
+ }
1392
+ c .cache .setDeviceInfo (cache )
1393
+
1394
+ return nil
1395
+ }
1396
+
1284
1397
func (c * MerakiClient ) parseDeviceStatus (devices []* deviceStatusWrapper ) ([]* kt.JCHF , error ) {
1285
1398
res := make ([]* kt.JCHF , 0 )
1286
1399
@@ -1301,6 +1414,10 @@ func (c *MerakiClient) parseDeviceStatus(devices []*deviceStatusWrapper) ([]*kt.
1301
1414
"mac" : wrap .device .Mac ,
1302
1415
"model" : wrap .device .Model ,
1303
1416
"product_type" : wrap .device .ProductType ,
1417
+ "lat" : fmt .Sprintf ("%f" , wrap .info .Lat ),
1418
+ "lng" : fmt .Sprintf ("%f" , wrap .info .Lng ),
1419
+ "address" : wrap .info .Address ,
1420
+ "notes" : wrap .info .Notes ,
1304
1421
}
1305
1422
1306
1423
dst .CustomInt = map [string ]int32 {}
0 commit comments