Collecting from network equipments

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)