2018-05-03 18:40:52 +02:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/apex/log"
|
2018-09-11 18:41:15 +02:00
|
|
|
"github.com/ooni/probe-cli/internal/database"
|
2019-12-02 14:15:50 +01:00
|
|
|
"github.com/ooni/probe-cli/utils"
|
2018-05-03 18:40:52 +02:00
|
|
|
)
|
|
|
|
|
2018-09-11 18:41:15 +02:00
|
|
|
func formatSpeed(speed float64) string {
|
2018-05-03 18:40:52 +02:00
|
|
|
if speed < 1000 {
|
2018-09-11 18:41:15 +02:00
|
|
|
return fmt.Sprintf("%.2f Kbit/s", speed)
|
2018-05-03 18:40:52 +02:00
|
|
|
} else if speed < 1000*1000 {
|
|
|
|
return fmt.Sprintf("%.2f Mbit/s", float32(speed)/1000)
|
|
|
|
} else if speed < 1000*1000*1000 {
|
|
|
|
return fmt.Sprintf("%.2f Gbit/s", float32(speed)/(1000*1000))
|
|
|
|
}
|
|
|
|
// WTF, you crazy?
|
|
|
|
return fmt.Sprintf("%.2f Tbit/s", float32(speed)/(1000*1000*1000))
|
|
|
|
}
|
|
|
|
|
2018-09-12 14:03:07 +02:00
|
|
|
func formatSize(size float64) string {
|
|
|
|
if size < 1024 {
|
|
|
|
return fmt.Sprintf("%.1fK", size)
|
|
|
|
} else if size < 1024*1024 {
|
|
|
|
return fmt.Sprintf("%.1fM", size/1024.0)
|
|
|
|
} else if size < 1024*1024*1024 {
|
|
|
|
return fmt.Sprintf("%.1fG", size/(1024.0*1024.0))
|
|
|
|
}
|
|
|
|
// WTF, you crazy?
|
|
|
|
return fmt.Sprintf("%.1fT", size/(1024*1024*1024))
|
|
|
|
}
|
|
|
|
|
2018-09-10 12:41:28 +02:00
|
|
|
var summarizers = map[string]func(uint64, uint64, string) []string{
|
|
|
|
"websites": func(totalCount uint64, anomalyCount uint64, ss string) []string {
|
2018-05-03 18:40:52 +02:00
|
|
|
return []string{
|
2018-09-10 12:41:28 +02:00
|
|
|
fmt.Sprintf("%d tested", totalCount),
|
|
|
|
fmt.Sprintf("%d blocked", anomalyCount),
|
2018-05-03 18:40:52 +02:00
|
|
|
"",
|
|
|
|
}
|
|
|
|
},
|
2018-09-10 12:41:28 +02:00
|
|
|
"performance": func(totalCount uint64, anomalyCount uint64, ss string) []string {
|
2018-09-11 18:41:15 +02:00
|
|
|
var tk database.PerformanceTestKeys
|
2018-09-10 12:41:28 +02:00
|
|
|
if err := json.Unmarshal([]byte(ss), &tk); err != nil {
|
2018-05-03 18:40:52 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return []string{
|
2018-09-10 12:41:28 +02:00
|
|
|
fmt.Sprintf("Download: %s", formatSpeed(tk.Download)),
|
|
|
|
fmt.Sprintf("Upload: %s", formatSpeed(tk.Upload)),
|
|
|
|
fmt.Sprintf("Ping: %.2fms", tk.Ping),
|
2018-05-03 18:40:52 +02:00
|
|
|
}
|
|
|
|
},
|
2018-09-10 12:41:28 +02:00
|
|
|
"im": func(totalCount uint64, anomalyCount uint64, ss string) []string {
|
2018-05-03 18:40:52 +02:00
|
|
|
return []string{
|
2018-09-10 12:41:28 +02:00
|
|
|
fmt.Sprintf("%d tested", totalCount),
|
|
|
|
fmt.Sprintf("%d blocked", anomalyCount),
|
2018-05-03 18:40:52 +02:00
|
|
|
"",
|
|
|
|
}
|
|
|
|
},
|
2018-09-10 12:41:28 +02:00
|
|
|
"middlebox": func(totalCount uint64, anomalyCount uint64, ss string) []string {
|
2018-05-03 18:40:52 +02:00
|
|
|
return []string{
|
2018-09-10 12:41:28 +02:00
|
|
|
fmt.Sprintf("Detected: %v", anomalyCount > 0),
|
2018-05-03 18:40:52 +02:00
|
|
|
"",
|
|
|
|
"",
|
|
|
|
}
|
|
|
|
},
|
2019-12-28 17:48:07 +01:00
|
|
|
"circumvention": func(totalCount uint64, anomalyCount uint64, ss string) []string {
|
|
|
|
return []string{
|
2020-01-07 15:13:13 +01:00
|
|
|
fmt.Sprintf("%d tested", totalCount),
|
|
|
|
fmt.Sprintf("%d blocked", anomalyCount),
|
2019-12-28 17:48:07 +01:00
|
|
|
"",
|
|
|
|
}
|
|
|
|
},
|
2018-05-03 18:40:52 +02:00
|
|
|
}
|
|
|
|
|
2018-09-10 12:41:28 +02:00
|
|
|
func makeSummary(name string, totalCount uint64, anomalyCount uint64, ss string) []string {
|
|
|
|
return summarizers[name](totalCount, anomalyCount, ss)
|
2018-05-03 18:40:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func logResultItem(w io.Writer, f log.Fields) error {
|
|
|
|
colWidth := 24
|
|
|
|
|
|
|
|
rID := f.Get("id").(int64)
|
|
|
|
name := f.Get("name").(string)
|
2018-09-17 13:04:48 +02:00
|
|
|
isDone := f.Get("is_done").(bool)
|
2018-05-03 18:40:52 +02:00
|
|
|
startTime := f.Get("start_time").(time.Time)
|
|
|
|
networkName := f.Get("network_name").(string)
|
2018-09-11 18:16:14 +02:00
|
|
|
asn := fmt.Sprintf("AS%d (%s)", f.Get("asn").(uint), f.Get("network_country_code").(string))
|
2018-05-03 18:40:52 +02:00
|
|
|
//runtime := f.Get("runtime").(float64)
|
|
|
|
//dataUsageUp := f.Get("dataUsageUp").(int64)
|
|
|
|
//dataUsageDown := f.Get("dataUsageDown").(int64)
|
|
|
|
index := f.Get("index").(int)
|
|
|
|
totalCount := f.Get("total_count").(int)
|
|
|
|
if index == 0 {
|
|
|
|
fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth*2+2)+"┓\n")
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(w, "┢"+strings.Repeat("━", colWidth*2+2)+"┪\n")
|
|
|
|
}
|
|
|
|
|
2019-12-02 14:15:50 +01:00
|
|
|
firstRow := utils.RightPad(fmt.Sprintf("#%d - %s", rID, startTime.Format(time.RFC822)), colWidth*2)
|
2018-05-03 18:40:52 +02:00
|
|
|
fmt.Fprintf(w, "┃ "+firstRow+" ┃\n")
|
|
|
|
fmt.Fprintf(w, "┡"+strings.Repeat("━", colWidth*2+2)+"┩\n")
|
|
|
|
|
2018-09-10 12:41:28 +02:00
|
|
|
summary := makeSummary(name,
|
|
|
|
f.Get("measurement_count").(uint64),
|
|
|
|
f.Get("measurement_anomaly_count").(uint64),
|
|
|
|
f.Get("test_keys").(string))
|
2018-05-03 18:40:52 +02:00
|
|
|
|
|
|
|
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
|
2019-12-02 14:15:50 +01:00
|
|
|
utils.RightPad(name, colWidth),
|
|
|
|
utils.RightPad(summary[0], colWidth)))
|
2018-05-03 18:40:52 +02:00
|
|
|
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
|
2019-12-02 14:15:50 +01:00
|
|
|
utils.RightPad(networkName, colWidth),
|
|
|
|
utils.RightPad(summary[1], colWidth)))
|
2018-05-03 18:40:52 +02:00
|
|
|
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
|
2019-12-02 14:15:50 +01:00
|
|
|
utils.RightPad(asn, colWidth),
|
|
|
|
utils.RightPad(summary[2], colWidth)))
|
2018-05-03 18:40:52 +02:00
|
|
|
|
|
|
|
if index == totalCount-1 {
|
2018-09-17 13:04:48 +02:00
|
|
|
if isDone == true {
|
|
|
|
fmt.Fprintf(w, "└┬──────────────┬──────────────┬──────────────────┬┘\n")
|
|
|
|
} else {
|
|
|
|
// We want the incomplete section to not have a footer
|
|
|
|
fmt.Fprintf(w, "└──────────────────────────────────────────────────┘\n")
|
|
|
|
}
|
2018-05-03 18:40:52 +02:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2018-06-22 11:53:10 +02:00
|
|
|
|
|
|
|
func logResultSummary(w io.Writer, f log.Fields) error {
|
|
|
|
|
|
|
|
networks := f.Get("total_networks").(int64)
|
|
|
|
tests := f.Get("total_tests").(int64)
|
2018-09-12 13:42:16 +02:00
|
|
|
dataUp := f.Get("total_data_usage_up").(float64)
|
|
|
|
dataDown := f.Get("total_data_usage_down").(float64)
|
2018-06-22 11:57:25 +02:00
|
|
|
if tests == 0 {
|
|
|
|
fmt.Fprintf(w, "No results\n")
|
|
|
|
fmt.Fprintf(w, "Try running:\n")
|
|
|
|
fmt.Fprintf(w, " ooni run websites\n")
|
|
|
|
return nil
|
|
|
|
}
|
2018-06-22 11:53:10 +02:00
|
|
|
// └┬──────────────┬──────────────┬──────────────┬
|
|
|
|
fmt.Fprintf(w, " │ %s │ %s │ %s │\n",
|
2019-12-02 14:15:50 +01:00
|
|
|
utils.RightPad(fmt.Sprintf("%d tests", tests), 12),
|
|
|
|
utils.RightPad(fmt.Sprintf("%d nets", networks), 12),
|
|
|
|
utils.RightPad(fmt.Sprintf("⬆ %s ⬇ %s", formatSize(dataUp), formatSize(dataDown)), 16))
|
2018-09-12 14:03:07 +02:00
|
|
|
fmt.Fprintf(w, " └──────────────┴──────────────┴──────────────────┘\n")
|
2018-06-22 11:53:10 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|