Add hooks for generating result summaries

This commit is contained in:
Arturo Filastò 2018-03-20 14:19:19 +01:00
parent a747b76ecf
commit ce0e077175
4 changed files with 88 additions and 20 deletions

View File

@ -44,12 +44,12 @@ func init() {
time.Now().UTC().Format(time.RFC3339Nano))) time.Now().UTC().Format(time.RFC3339Nano)))
ctl := nettests.NewController(nt, ctx, result, msmtPath) ctl := nettests.NewController(nt, ctx, result, msmtPath)
if err := nt.Run(ctl); err != nil { if err = nt.Run(ctl); err != nil {
log.WithError(err).Errorf("Failed to run %s", group.Label) log.WithError(err).Errorf("Failed to run %s", group.Label)
return err return err
} }
} }
if err = result.Finished(ctx.DB); err != nil { if err = result.Finished(ctx.DB, group.Summary); err != nil {
return err return err
} }
return nil return nil

View File

@ -12,6 +12,12 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// ResultSummaryFunc is the function used to generate result summaries
type ResultSummaryFunc func(SummaryMap) (string, error)
// SummaryMap contains a mapping from test name to serialized summary for it
type SummaryMap map[string]string
// UpdateOne will run the specified update query and check that it only affected one row // UpdateOne will run the specified update query and check that it only affected one row
func UpdateOne(db *sqlx.DB, query string, arg interface{}) error { func UpdateOne(db *sqlx.DB, query string, arg interface{}) error {
res, err := db.NamedExec(query, arg) res, err := db.NamedExec(query, arg)
@ -187,17 +193,43 @@ type Result struct {
MeasurementDir string `db:"measurement_dir"` MeasurementDir string `db:"measurement_dir"`
} }
// MakeSummaryMap return a mapping of test names to summaries for the given
// result
func MakeSummaryMap(db *sqlx.DB, r *Result) (SummaryMap, error) {
summaryMap := SummaryMap{}
msmts := []Measurement{}
// XXX maybe we only want to select some of the columns
err := db.Select(&msmts, "SELECT name, summary FROM measurements WHERE result_id = $1", r.ID)
if err != nil {
return nil, errors.Wrap(err, "failed to get measurements")
}
for _, msmt := range msmts {
summaryMap[msmt.Name] = msmt.Summary
}
return summaryMap, nil
}
// Finished marks the result as done and sets the runtime // Finished marks the result as done and sets the runtime
func (r *Result) Finished(db *sqlx.DB) error { func (r *Result) Finished(db *sqlx.DB, makeSummary ResultSummaryFunc) error {
if r.Done == true || r.Runtime != 0 { if r.Done == true || r.Runtime != 0 {
return errors.New("Result is already finished") return errors.New("Result is already finished")
} }
r.Runtime = time.Now().UTC().Sub(r.StartTime).Seconds() r.Runtime = time.Now().UTC().Sub(r.StartTime).Seconds()
r.Done = true r.Done = true
// XXX add in here functionality to compute the summary // XXX add in here functionality to compute the summary
summaryMap, err := MakeSummaryMap(db, r)
if err != nil {
return err
}
err := UpdateOne(db, `UPDATE results r.Summary, err = makeSummary(summaryMap)
SET done = :done, runtime = :runtime if err != nil {
return err
}
err = UpdateOne(db, `UPDATE results
SET done = :done, runtime = :runtime, summary = :summary
WHERE id = :id`, r) WHERE id = :id`, r)
if err != nil { if err != nil {
return errors.Wrap(err, "updating finished result") return errors.Wrap(err, "updating finished result")

View File

@ -1,6 +1,10 @@
package groups package groups
import ( import (
"encoding/json"
"github.com/apex/log"
"github.com/openobservatory/gooni/internal/database"
"github.com/openobservatory/gooni/nettests" "github.com/openobservatory/gooni/nettests"
"github.com/openobservatory/gooni/nettests/performance" "github.com/openobservatory/gooni/nettests/performance"
"github.com/openobservatory/gooni/nettests/websites" "github.com/openobservatory/gooni/nettests/websites"
@ -10,7 +14,15 @@ import (
type NettestGroup struct { type NettestGroup struct {
Label string Label string
Nettests []nettests.Nettest Nettests []nettests.Nettest
Summary func(s string) string Summary database.ResultSummaryFunc
}
// PerformanceSummary is the result summary for a performance test
type PerformanceSummary struct {
Upload int64
Download int64
Ping float64
Bitrate int64
} }
// NettestGroups that can be run by the user // NettestGroups that can be run by the user
@ -20,8 +32,8 @@ var NettestGroups = map[string]NettestGroup{
Nettests: []nettests.Nettest{ Nettests: []nettests.Nettest{
websites.WebConnectivity{}, websites.WebConnectivity{},
}, },
Summary: func(s string) string { Summary: func(m database.SummaryMap) (string, error) {
return "{}" return "{}", nil
}, },
}, },
"performance": NettestGroup{ "performance": NettestGroup{
@ -30,22 +42,46 @@ var NettestGroups = map[string]NettestGroup{
performance.Dash{}, performance.Dash{},
performance.NDT{}, performance.NDT{},
}, },
Summary: func(s string) string { Summary: func(m database.SummaryMap) (string, error) {
return "{}" var (
err error
ndtSummary performance.NDTSummary
dashSummary performance.DashSummary
summary PerformanceSummary
)
err = json.Unmarshal([]byte(m["Dash"]), &dashSummary)
if err != nil {
log.WithError(err).Error("failed to unmarshal Dash summary")
return "", err
}
err = json.Unmarshal([]byte(m["Ndt"]), &ndtSummary)
if err != nil {
log.WithError(err).Error("failed to unmarshal Dash summary")
return "", err
}
summary.Bitrate = dashSummary.Bitrate
summary.Download = ndtSummary.Download
summary.Upload = ndtSummary.Upload
summary.Ping = ndtSummary.AvgRTT
summaryBytes, err := json.Marshal(summary)
if err != nil {
return "", err
}
return string(summaryBytes), nil
}, },
}, },
"middleboxes": NettestGroup{ "middleboxes": NettestGroup{
Label: "Middleboxes", Label: "Middleboxes",
Nettests: []nettests.Nettest{}, Nettests: []nettests.Nettest{},
Summary: func(s string) string { Summary: func(m database.SummaryMap) (string, error) {
return "{}" return "{}", nil
}, },
}, },
"im": NettestGroup{ "im": NettestGroup{
Label: "Instant Messaging", Label: "Instant Messaging",
Nettests: []nettests.Nettest{}, Nettests: []nettests.Nettest{},
Summary: func(s string) string { Summary: func(m database.SummaryMap) (string, error) {
return "{}" return "{}", nil
}, },
}, },
} }

View File

@ -21,9 +21,9 @@ type NDTSummary struct {
Upload int64 Upload int64
Download int64 Download int64
Ping int64 Ping int64
MaxRTT int64 MaxRTT float64
AvgRTT int64 AvgRTT float64
MinRTT int64 MinRTT float64
MSS int64 MSS int64
OutOfOrder int64 OutOfOrder int64
PacketLoss float64 PacketLoss float64
@ -39,9 +39,9 @@ func (n NDT) Summary(tk map[string]interface{}) interface{} {
Upload: int64(simple["upload"].(float64)), Upload: int64(simple["upload"].(float64)),
Download: int64(simple["download"].(float64)), Download: int64(simple["download"].(float64)),
Ping: int64(simple["ping"].(float64)), Ping: int64(simple["ping"].(float64)),
MaxRTT: int64(advanced["max_rtt"].(float64)), MaxRTT: advanced["max_rtt"].(float64),
AvgRTT: int64(advanced["avg_rtt"].(float64)), AvgRTT: advanced["avg_rtt"].(float64),
MinRTT: int64(advanced["min_rtt"].(float64)), MinRTT: advanced["min_rtt"].(float64),
MSS: int64(advanced["mss"].(float64)), MSS: int64(advanced["mss"].(float64)),
OutOfOrder: int64(advanced["out_of_order"].(float64)), OutOfOrder: int64(advanced["out_of_order"].(float64)),
PacketLoss: advanced["packet_loss"].(float64), PacketLoss: advanced["packet_loss"].(float64),