blob: ccfd0bca0095d56beb3f03ba61fc4337927a9e0b [file] [log] [blame]
Serge Bazanski8d7843c2018-10-04 10:37:36 +01001package main
2
Serge Bazanski46765082018-10-06 12:32:01 +01003import (
Serge Bazanskic7be4a12018-10-06 13:18:05 +01004 "context"
5 "fmt"
Serge Bazanski46765082018-10-06 12:32:01 +01006 "net/http"
Serge Bazanski69518ab2018-10-06 17:48:23 +01007 "sort"
Serge Bazanskide869df2018-10-06 13:55:49 +01008 "strings"
Serge Bazanski8d7843c2018-10-04 10:37:36 +01009
Serge Bazanski46765082018-10-06 12:32:01 +010010 "code.hackerspace.pl/q3k/topo/graph"
Serge Bazanskic7be4a12018-10-06 13:18:05 +010011 "github.com/gobuffalo/packr"
Serge Bazanski46765082018-10-06 12:32:01 +010012 "github.com/golang/glog"
Serge Bazanskic7be4a12018-10-06 13:18:05 +010013 "github.com/q3k/statusz"
Serge Bazanski69518ab2018-10-06 17:48:23 +010014 "vbom.ml/util/sortorder"
Serge Bazanski46765082018-10-06 12:32:01 +010015)
16
17type ServiceConfig struct {
18 DebugListen string
19}
20
21type Service struct {
22 gr *graph.Graph
23 config ServiceConfig
24}
25
26func NewService(gr *graph.Graph, c ServiceConfig) *Service {
27 return &Service{
28 gr: gr,
29 config: c,
30 }
31}
32
Serge Bazanskic7be4a12018-10-06 13:18:05 +010033type TopologyStatus struct {
34}
35
36const topologyFragment = `
37 <script src="/assets/viz.js"></script>
38 <script>
39
40 var viz = new Viz({ workerURL: '/assets/full.render.js' });
41 var xmlhttp = new XMLHttpRequest();
42 xmlhttp.onreadystatechange = function() {
43 if (this.readyState == 4 && this.status == 200) {
44 var dot = this.responseText;
45 viz.renderSVGElement(dot)
46 .then(function(element) {
Serge Bazanski69518ab2018-10-06 17:48:23 +010047 document.getElementById("graph").appendChild(element);
Serge Bazanskic7be4a12018-10-06 13:18:05 +010048 });
49 }
50 };
51 xmlhttp.open('GET', '/debug/graphviz');
52 xmlhttp.send();
53
54 </script>
Serge Bazanski69518ab2018-10-06 17:48:23 +010055 <div id="graph" style="text-align: center;"></div>
Serge Bazanskic7be4a12018-10-06 13:18:05 +010056`
57
Serge Bazanski46765082018-10-06 12:32:01 +010058func (s *Service) Run() {
Serge Bazanskic7be4a12018-10-06 13:18:05 +010059 assets := packr.NewBox("./assets")
60 http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(assets)))
61
Serge Bazanski46765082018-10-06 12:32:01 +010062 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
63 http.Redirect(w, r, "/debug/status", http.StatusSeeOther)
64 })
Serge Bazanskic7be4a12018-10-06 13:18:05 +010065
66 http.HandleFunc("/debug/graphviz", func(w http.ResponseWriter, r *http.Request) {
67 fmt.Fprintf(w, "graph G {\n")
Serge Bazanski69518ab2018-10-06 17:48:23 +010068 fmt.Fprintf(w, " ranksep = 2\n")
69 fmt.Fprintf(w, " splines = polyline\n")
Serge Bazanskide869df2018-10-06 13:55:49 +010070 fmt.Fprintf(w, " rankdir = LR\n")
Serge Bazanskic7be4a12018-10-06 13:18:05 +010071 for _, machine := range s.gr.Machines {
Serge Bazanskide869df2018-10-06 13:55:49 +010072 portNames := []string{}
Serge Bazanskic7be4a12018-10-06 13:18:05 +010073 for _, port := range machine.Ports {
Serge Bazanskide869df2018-10-06 13:55:49 +010074 name := fmt.Sprintf("<%s> %s", port.Name, port.Name)
75 portNames = append(portNames, name)
Serge Bazanskic7be4a12018-10-06 13:18:05 +010076 }
Serge Bazanskide869df2018-10-06 13:55:49 +010077 ports := strings.Join(portNames, "|")
78 fmt.Fprintf(w, " %s [shape=record label=\"{ %s | { %s }}\"]\n", machine.Name, machine.Name, ports)
Serge Bazanskic7be4a12018-10-06 13:18:05 +010079 }
80 for _, sw := range s.gr.Switches {
Serge Bazanskide869df2018-10-06 13:55:49 +010081 portNames := []string{}
Serge Bazanski69518ab2018-10-06 17:48:23 +010082 portsOrdered := []*graph.SwitchPort{}
Serge Bazanskic7be4a12018-10-06 13:18:05 +010083 for _, port := range sw.Ports {
Serge Bazanski69518ab2018-10-06 17:48:23 +010084 portsOrdered = append(portsOrdered, port)
85 }
86 sort.Slice(portsOrdered, func(i, j int) bool {
87 return sortorder.NaturalLess(portsOrdered[i].Name, portsOrdered[j].Name)
88 })
89 for _, port := range portsOrdered {
Serge Bazanskide869df2018-10-06 13:55:49 +010090 name := fmt.Sprintf("<%s> %s", port.Name, port.Name)
91 portNames = append(portNames, name)
Serge Bazanskic7be4a12018-10-06 13:18:05 +010092 }
Serge Bazanskide869df2018-10-06 13:55:49 +010093 ports := strings.Join(portNames, "|")
94 fmt.Fprintf(w, " %s [shape=record label=\"{{ %s } | %s}\"]\n", sw.Name, ports, sw.Name)
Serge Bazanskic7be4a12018-10-06 13:18:05 +010095 }
96 for _, machine := range s.gr.Machines {
97 for _, port := range machine.Ports {
98 if port.OtherEnd == nil {
99 continue
100 }
Serge Bazanski69518ab2018-10-06 17:48:23 +0100101 fmt.Fprintf(w, " %s:%q:e -- %s:%q:w\n", machine.Name, port.Name, port.OtherEnd.Switch.Name, port.OtherEnd.Name)
Serge Bazanskic7be4a12018-10-06 13:18:05 +0100102 }
103 }
104 fmt.Fprintf(w, "}\n")
105 })
106
107 statusz.AddStatusPart("Topology", topologyFragment, func(ctx context.Context) interface{} {
108 return &TopologyStatus{}
109 })
Serge Bazanski46765082018-10-06 12:32:01 +0100110 glog.Infof("Debug listening on %s....", s.config.DebugListen)
111 http.ListenAndServe(s.config.DebugListen, nil)
Serge Bazanski8d7843c2018-10-04 10:37:36 +0100112}