graph2prom.sml 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. structure Main :
  2. sig
  3. val main: string * string list -> OS.Process.status
  4. end =
  5. struct
  6. structure J = JSON
  7. structure JP = JSONParser
  8. structure JU = JSONUtil
  9. structure M : MESH = Mesh
  10. structure PC = PromConfig
  11. val link_prefix = PC.link_prefix
  12. val mesh_prefix = PC.mesh_prefix
  13. val newline = String.str #"\n"
  14. val link_header =
  15. "# HELP " ^ link_prefix ^ " link quality between two nodes" ^ newline ^
  16. "# TYPE " ^ link_prefix ^ " gauge" ^ newline
  17. val mesh_header =
  18. "# HELP " ^ mesh_prefix ^ " members and (nodes + edges / 1000) of a mesh" ^ newline ^
  19. "# TYPE " ^ mesh_prefix ^ " gauge" ^ newline
  20. fun prom2string (metric, labels, scalar) =
  21. let fun esc #"\"" = "\\\""
  22. | esc #"\\" = "\\\\"
  23. | esc #"\n" = "\\n"
  24. | esc c = str c
  25. in metric ^
  26. (ListFormat.fmt {init = "{", sep= ",", final = "}",
  27. fmt = fn (label, value) => label ^ "=\"" ^ (String.translate esc value) ^ "\""}
  28. labels) ^ " " ^
  29. scalar ^ (* " " ^
  30. timestamp ^ *) newline
  31. end
  32. (* exnMessage from JSONUtil - slightly extended *)
  33. fun v2bs' (J.ARRAY []) = "[]"
  34. | v2bs' (J.ARRAY _) = "[...]"
  35. | v2bs' (J.BOOL v) = Bool.toString v
  36. | v2bs' (J.FLOAT v) = Real.toString v
  37. | v2bs' (J.INT v) = IntInf.toString v
  38. | v2bs' J.NULL = "null"
  39. | v2bs' (J.OBJECT []) = "{}"
  40. | v2bs' (J.OBJECT _) = "{...}"
  41. | v2bs' (J.STRING v) = v
  42. fun v2bs (J.ARRAY []) = "[]"
  43. | v2bs (J.ARRAY vl) =
  44. ListFormat.fmt { init = "[", sep = ",", final = "]", fmt = v2bs' } vl
  45. | v2bs (J.OBJECT []) = "{}"
  46. | v2bs (J.OBJECT fl) =
  47. ListFormat.fmt { init = "{", sep = ",", final = "}",
  48. fmt = fn (n, v) => n ^ ":" ^ (v2bs' v) } fl
  49. | v2bs v = v2bs' v
  50. fun json_handler logstring exn =
  51. (TextIO.output (TextIO.stdErr,
  52. "json_handler(" ^ logstring ^ "): " ^ (JU.exnMessage exn) ^ ": " ^
  53. (v2bs (case exn
  54. of JU.NotBool v => v
  55. | JU.NotInt v => v
  56. | JU.NotNumber v => v
  57. | JU.NotString v => v
  58. | JU.NotObject v => v
  59. | JU.FieldNotFound(v, fld) => v
  60. | JU.NotArray v => v
  61. | _ => raise exn)) ^
  62. "\n") ; ())
  63. fun get_version obj = JU.asInt (JU.lookupField obj "version")
  64. fun get_fields (J.OBJECT flds) = flds
  65. | get_fields v = raise Fail ("value is not an object (" ^ (v2bs v) ^ ")")
  66. fun get_list (J.OBJECT flds) = map #2 flds
  67. | get_list (J.ARRAY nds) = nds
  68. | get_list v = raise Fail ("value does not contain a list (" ^ (v2bs v) ^ ")")
  69. fun get_nodes obj = get_list (JU.lookupField (JU.lookupField obj "batadv") "nodes")
  70. fun get_links obj = get_list (JU.lookupField (JU.lookupField obj "batadv") "links")
  71. fun extract_node obj = { id = JU.asString (JU.lookupField obj "id"),
  72. node_id = JU.asString (JU.lookupField obj "node_id") }
  73. fun extract_link obj = { source = JU.asInt (JU.lookupField obj "source"),
  74. target = JU.asInt (JU.lookupField obj "target"),
  75. tq = JU.asNumber (JU.lookupField obj "tq"),
  76. vpn = JU.asBool (JU.lookupField obj "vpn") }
  77. fun link2prom nodes_id_vector { source, target, tq, vpn } =
  78. let val source_id = Vector.sub (nodes_id_vector, source)
  79. val target_id = Vector.sub (nodes_id_vector, target)
  80. in prom2string (link_prefix,
  81. [("source", source_id), ("target", target_id), ("vpn", Bool.toString vpn)],
  82. Real.toString tq)
  83. end
  84. fun mesh2prom nodes_id_vector { nodes, edges } =
  85. let val node_count = Real.fromInt (M.Set.numItems nodes)
  86. val edge_count = Real.fromInt (M.PairSet.numItems edges)
  87. val members = ListMergeSort.sort String.>
  88. (map (fn i => Vector.sub (nodes_id_vector, i))
  89. (M.Set.listItems nodes))
  90. in prom2string (mesh_prefix,
  91. [("node_id", hd members),
  92. ("members", ListFormat.fmt { init = "", final = "", sep = "|",
  93. fmt = fn m => m }
  94. members)],
  95. Real.toString (node_count + edge_count / 1000.0))
  96. end
  97. fun complain (p, s) =
  98. (TextIO.output (TextIO.stdErr, concat [p, ": ", s, "\n"]);
  99. OS.Process.failure)
  100. fun main (p, [inf]) =
  101. (let val json = JP.parseFile inf
  102. val _ = get_version json = 1 orelse raise Fail "version must be 1"
  103. val nodes_json = get_nodes json
  104. handle exn => (json_handler "get_nodes" exn ; raise Fail "get_nodes")
  105. val nodes_vector = Vector.fromList (map extract_node nodes_json)
  106. val nodes_id_vector = Vector.map #node_id nodes_vector
  107. val links_json = get_links json
  108. handle exn => (json_handler "get_links" exn ; raise Fail "get_links")
  109. val links_extract = map extract_link links_json
  110. val links_prom = map (link2prom nodes_id_vector) links_extract
  111. val graph = M.links2graph (map (fn l => (#source l, #target l))
  112. links_extract)
  113. val meshes_prom = map (mesh2prom nodes_id_vector)
  114. (M.Map.listItems (#meshes graph))
  115. in (print link_header ;
  116. print mesh_header ;
  117. app print links_prom ;
  118. app print meshes_prom ;
  119. OS.Process.success)
  120. end handle e => complain (p, exnMessage e))
  121. | main (p, _) = complain (p, "usage: " ^ p ^ " graph.json")
  122. end