Network equipments such as switches know which equipments are connected to them using MAC address tables associated to ports. This can be used to locate those equipments and it enables also to check that the switch configuration is the one required (VLAN, PoE, speed,...). Unfortunately, such tables have to be automatically purged in the network equipments. Luckily, there are network equipments with firmwares allowing to interact with them with a REST API using JSON.
The Data Worker has to periodically collect MAC tables using the REST API then transform and send the resulting data to another Data Worker acting as an HTTP server. The second Data Worker can then propose a dedicated dashboard.
for $i in 1 to 10000 let $step := ( let $pcs := doc('collect.json')?* let $switches := doc('switches.json')?* let $login := doc('login.json') let $result := map { for $switch in $switches let $m := fn:doc('http://' || xs:string($switch?ip) || ':80/rest/v3/login-sessions', map {'method': 'json', 'http-verb': 'POST', 'timeout': '3000'}, map {'userName': xs:string($login?user), 'password': xs:string($login?password)}) return (if ($m?cookie) then ( let $sessionId := map {'method': 'json', 'http-verb': 'GET', 'http-cookie': xs:string($m?cookie)} let $vps := doc('http://' || xs:string($switch?ip) || ':80/rest/v3/vlans-ports', $sessionId)?vlan_port_element?* let $macs := doc('http://' || xs:string($switch?ip) || ':80/rest/v3/mac-table', $sessionId)?mac_table_entry_element?* let $trk := $vps[?port_mode eq 'POM_TAGGED_STATIC' and ?vlan_id ne 5] ! xs:string(?port_id) for $port in $vps[not(?port_id = $trk)] ! ?port_id for $vlanid in $vps[?port_id eq $port] ! ?vlan_id let $portmacs := $macs[?port_id eq $port] ! ietf:mac(?mac_address) return if (not(empty($portmacs))) then ( for $portmac in $portmacs let $pcname := local-name(head($pcs[ietf:mac(?mac) eq $portmac])) let $ename := (if ($pcname) then $pcname else '#' || xs:string($portmac)) return entry {$ename} {map{'switch': local-name($switch), 'port': xs:string($port), 'vlan': xs:string($vlanid)} } ) else ()) else trace((), local-name($switch) || ': Connection refused. ')) } let $t := (if (exists($result?*)) then ( trace((), xs:string(current-dateTime()) || ' ' || count(http:send-request(<http:request method='post'/>, 'http://switchmanager:5000/batchcollect.xqy', $result)[2]/node()?*) || ' MAC entries found. ') ) else trace((), xs:string(current-dateTime()) || ' ' || 'No entry. ')) let $pause := prof:sleep(1000 * 60 * 3) return () ) return ()
declare %updating function local:mergejsonfile($path, $batch) { let $doc := doc($path) let $m := $doc/map() let $update := ( for $item in $batch?* return (if ($m?(local-name($item))) then replace node $m?(local-name($item))/node() with map:merge(($item/node(), $m?(local-name($item))/node())) else insert node entry {local-name($item)} {$item/node()} into $m)) let $write := file:write($path, $doc, map {"indent" : "yes"}) return $batch }; let $filename := 'collect.json' let $batch := request:body-doc() let $tstamp := xs:string(current-dateTime()) let $addstamp := (for $item in $batch?* return (insert node entry btimestamp {$tstamp} into $item/node(), ())) return local:mergejsonfile($filename, $batch)