blob: 916299cfe3e1dfffbb5088d0b44fedcea067140f [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 Bazanskic7be4a12018-10-06 13:18:05 +010010 "github.com/gobuffalo/packr"
Serge Bazanski46765082018-10-06 12:32:01 +010011 "github.com/golang/glog"
Serge Bazanskic7be4a12018-10-06 13:18:05 +010012 "github.com/q3k/statusz"
Serge Bazanski69518ab2018-10-06 17:48:23 +010013 "vbom.ml/util/sortorder"
Serge Bazanskia758ef52018-10-06 17:54:25 +010014
15 "code.hackerspace.pl/q3k/topo/graph"
16 "code.hackerspace.pl/q3k/topo/state"
Serge Bazanski46765082018-10-06 12:32:01 +010017)
18
19type ServiceConfig struct {
20 DebugListen string
21}
22
23type Service struct {
24 gr *graph.Graph
Serge Bazanskia758ef52018-10-06 17:54:25 +010025 stm *state.StateManager
Serge Bazanski46765082018-10-06 12:32:01 +010026 config ServiceConfig
27}
28
Serge Bazanskia758ef52018-10-06 17:54:25 +010029func NewService(gr *graph.Graph, stm *state.StateManager, c ServiceConfig) *Service {
Serge Bazanski46765082018-10-06 12:32:01 +010030 return &Service{
31 gr: gr,
Serge Bazanskia758ef52018-10-06 17:54:25 +010032 stm: stm,
Serge Bazanski46765082018-10-06 12:32:01 +010033 config: c,
34 }
35}
36
Serge Bazanskic7be4a12018-10-06 13:18:05 +010037type TopologyStatus struct {
38}
39
40const topologyFragment = `
41 <script src="/assets/viz.js"></script>
42 <script>
43
44 var viz = new Viz({ workerURL: '/assets/full.render.js' });
45 var xmlhttp = new XMLHttpRequest();
46 xmlhttp.onreadystatechange = function() {
47 if (this.readyState == 4 && this.status == 200) {
48 var dot = this.responseText;
49 viz.renderSVGElement(dot)
50 .then(function(element) {
Serge Bazanski69518ab2018-10-06 17:48:23 +010051 document.getElementById("graph").appendChild(element);
Serge Bazanskic7be4a12018-10-06 13:18:05 +010052 });
53 }
54 };
55 xmlhttp.open('GET', '/debug/graphviz');
56 xmlhttp.send();
57
58 </script>
Serge Bazanski69518ab2018-10-06 17:48:23 +010059 <div id="graph" style="text-align: center;"></div>
Serge Bazanskic7be4a12018-10-06 13:18:05 +010060`
61
Serge Bazanski46765082018-10-06 12:32:01 +010062func (s *Service) Run() {
Serge Bazanskic7be4a12018-10-06 13:18:05 +010063 assets := packr.NewBox("./assets")
64 http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(assets)))
65
Serge Bazanski46765082018-10-06 12:32:01 +010066 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
67 http.Redirect(w, r, "/debug/status", http.StatusSeeOther)
68 })
Serge Bazanskic7be4a12018-10-06 13:18:05 +010069
70 http.HandleFunc("/debug/graphviz", func(w http.ResponseWriter, r *http.Request) {
71 fmt.Fprintf(w, "graph G {\n")
Serge Bazanski69518ab2018-10-06 17:48:23 +010072 fmt.Fprintf(w, " ranksep = 2\n")
73 fmt.Fprintf(w, " splines = polyline\n")
Serge Bazanskide869df2018-10-06 13:55:49 +010074 fmt.Fprintf(w, " rankdir = LR\n")
Serge Bazanskic7be4a12018-10-06 13:18:05 +010075 for _, machine := range s.gr.Machines {
Serge Bazanskide869df2018-10-06 13:55:49 +010076 portNames := []string{}
Serge Bazanskic7be4a12018-10-06 13:18:05 +010077 for _, port := range machine.Ports {
Serge Bazanskide869df2018-10-06 13:55:49 +010078 name := fmt.Sprintf("<%s> %s", port.Name, port.Name)
79 portNames = append(portNames, name)
Serge Bazanskic7be4a12018-10-06 13:18:05 +010080 }
Serge Bazanskide869df2018-10-06 13:55:49 +010081 ports := strings.Join(portNames, "|")
82 fmt.Fprintf(w, " %s [shape=record label=\"{ %s | { %s }}\"]\n", machine.Name, machine.Name, ports)
Serge Bazanskic7be4a12018-10-06 13:18:05 +010083 }
84 for _, sw := range s.gr.Switches {
Serge Bazanskide869df2018-10-06 13:55:49 +010085 portNames := []string{}
Serge Bazanski69518ab2018-10-06 17:48:23 +010086 portsOrdered := []*graph.SwitchPort{}
Serge Bazanskic7be4a12018-10-06 13:18:05 +010087 for _, port := range sw.Ports {
Serge Bazanski69518ab2018-10-06 17:48:23 +010088 portsOrdered = append(portsOrdered, port)
89 }
90 sort.Slice(portsOrdered, func(i, j int) bool {
91 return sortorder.NaturalLess(portsOrdered[i].Name, portsOrdered[j].Name)
92 })
93 for _, port := range portsOrdered {
Serge Bazanskide869df2018-10-06 13:55:49 +010094 name := fmt.Sprintf("<%s> %s", port.Name, port.Name)
95 portNames = append(portNames, name)
Serge Bazanskic7be4a12018-10-06 13:18:05 +010096 }
Serge Bazanskide869df2018-10-06 13:55:49 +010097 ports := strings.Join(portNames, "|")
98 fmt.Fprintf(w, " %s [shape=record label=\"{{ %s } | %s}\"]\n", sw.Name, ports, sw.Name)
Serge Bazanskic7be4a12018-10-06 13:18:05 +010099 }
100 for _, machine := range s.gr.Machines {
101 for _, port := range machine.Ports {
102 if port.OtherEnd == nil {
103 continue
104 }
Serge Bazanski69518ab2018-10-06 17:48:23 +0100105 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 +0100106 }
107 }
108 fmt.Fprintf(w, "}\n")
109 })
110
111 statusz.AddStatusPart("Topology", topologyFragment, func(ctx context.Context) interface{} {
112 return &TopologyStatus{}
113 })
Serge Bazanski46765082018-10-06 12:32:01 +0100114 glog.Infof("Debug listening on %s....", s.config.DebugListen)
115 http.ListenAndServe(s.config.DebugListen, nil)
Serge Bazanski8d7843c2018-10-04 10:37:36 +0100116}