Skip to content

Commit 6e21376

Browse files
Merge pull request #3 from robert-kisteleki/feature/measurement-scheduling
Feature/measurement scheduling
2 parents 098aa29 + 65f4b3b commit 6e21376

File tree

9 files changed

+887
-44
lines changed

9 files changed

+887
-44
lines changed

CHANGES.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
## next
44

5-
* NEW: add support for measurement status checks (status subcommand)
5+
* NEW: add support for measurement status checks (`status` subcommand)
6+
* NEW: add support for measurement scheduling (`measure` subcommand)
7+
* support all measurement types with lots of options and sane defaults
8+
* support all probe selection criteria
9+
* support default probe selection specification in config file
610

711
## v0.3.0
812

README.md

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ It supports:
66
* finding probes
77
* finding anchors
88
* finding measurements
9-
* downloading and displaying results of measurements and turning them into Go objects
10-
* tuning in to result streaming and turning them into Go objects
11-
* loading a local file containing measurement results and turning them into Go objects
9+
* scheduling new measurements
10+
* downloading and displaying results of measurements
11+
* tuning in to result streaming
12+
* loading a local file containing measurement results
1213
* various kinds of output formatters for displaying and aggregating measurement results
1314
* (more features to come)
1415

@@ -146,9 +147,96 @@ The output formatters are extensible, feel free to write your own -- and contrib
146147

147148
New output processors need to be registered in `goatcli.go`. See `some.go` or `native.go` for examples.
148149

150+
## Schedule a New Measurement
151+
152+
Use the `measurement` subcommand. Quick examples:
153+
154+
```sh
155+
# create a one-off with default set of probes
156+
$ ./goatcli measure --ping --target ping.ripe.net
157+
158+
# create an ongoing with default set of probes and predefined start and stop times
159+
$ ./goatcli measure --ping --target ping.ripe.net --ongoing --start 2023-10-23T19 --stop tomorrow
160+
161+
# create a one-off with 15 probes from AS33260
162+
$ ./goatcli measure --ping --target ping.ripe.net --probeasn 15@3320
163+
164+
# create a one-off with 15 probes from AS33260 and 20 probes from NL
165+
$ ./goatcli measure --trace --target ping.ripe.net --probeasn 15@3320 --probecc 20@nl
166+
```
167+
168+
### Authentication
169+
170+
A valid API key for creating measurements is needed. It should be defined in the configuration file (`/.config/goat.ini`):
171+
172+
```
173+
[apikeys]
174+
# Create new measurement(s)
175+
create_measurements = "12345678-xxxx-xxxx-xxxx-something"
176+
```
177+
178+
It is also possible to supply a key on the command line (`--key KEY`).
179+
180+
### Probe Selection
181+
182+
The following probe selection options are available:
183+
* `--probearea` select from areas. A comma separated list of `amount@area` where `area` can be `ww`, `west`, `nc`, `sc`, `ne`, `se`
184+
* `--probeasn` select from ASNs. A comma separated list of `amount@ASN`
185+
* `--probecc` select from countries. A comma separated list of `amount@CC` where CC is a valid country code
186+
* `--probelist` provides an explicit list of probe IDs to include as a comma separated list.
187+
* `--probeprefix` select from prefixes (IPv4 or IPv6). A comma separated list of `amount@prefix`
188+
* `--probereuse` reuse probes from a precvious measurement. A comma separated list of `amount@msmID`
189+
190+
Multiple probe selection criteria can be sepcified; each of them add more probes to the selection.
191+
192+
`--probetaginc` and `--probetagexc` can be used to filter for probes that have been tagged (or not tagged) with those tags. Both are comma separated lists.
193+
194+
A default probe selection can be expressed in the configuration file (`/.config/goat.ini`) using entries with the above names in the `[probespec]` section, e.g.:
195+
196+
```
197+
# default probe specifications for new measurements
198+
[probespec]
199+
probecc = ""
200+
probearea = "9@ww"
201+
probeasn = ""
202+
probeprefix = ""
203+
probelist = ""
204+
probereuse = ""
205+
```
206+
207+
### Timing
208+
209+
By default one-offs are scheduled, starting as soon as possible. You can specify a start time (`start`) in the future and for ongoings perhaps even a stop time (`stop`). Times can be specifies as:
210+
* UNIX timestamps
211+
* ISO8601 variants:
212+
* `YYYY-mm-ddTHH:MM:SS` - leaving time details from the right makes them default to 0 (e.g. `2023-10-24` is valid and becomes `2023-10-24T00:00:00`)
213+
214+
### Measurement Types and Options
215+
216+
You can define one measurement per invocation. This can be one of: `ping`, `trace`, `dns`, `tls`, `ntp` or `http` (the last one with restrictions by the system). Each measurement needs a `target`, except DNS that needs a `name` to look up and uses `target` as the server/resolver to use if it's specified, otherwise uses the local resolver.
217+
218+
Common options for measurements include `interval`, `spread`, `tags` and some more.
219+
220+
Each mesurement type accepts a number of options, such as `abuf`, `qbuf`, `nsid`, `rd` for DNS, `minhop` and `maxhop` for trace, etc. Check the help page for the complete list of these.
221+
222+
### Output
223+
224+
For now the measurement scheduling request returns either the ID of the new measurement or an error if something went wrong.
225+
226+
227+
## Status Checks
228+
229+
Provide a short summary of the results for a [measurement status check](https://atlas.ripe.net/docs/apis/rest-api-manual/measurements/status-checks.html). It needs a measurement ID, and by default it summarises the results (is there an alert, how many probes are in that state out of how many total, and with the `most` output formatter the list of alerting probes).
230+
231+
```sh
232+
$ ./goatcli status -id 61953517 -output most
233+
true 1 9 [1005382]
234+
```
235+
236+
149237
# Future Additions / TODO
150238

151-
* schedule a new measurement, stop existing measurements
239+
* stop existing measurements
152240
* modify participants of an existing measurement (add/remove probes)
153241
* check credit balance, transfer credits, ...
154242

command.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ func commandSelector() {
3636
commandResult(args[1:])
3737
case args[0] == "status":
3838
commandStatusCheck(args[1:])
39+
case args[0] == "measure":
40+
commandMeasure(args[1:])
3941
default:
4042
commandHelp()
4143
}
@@ -61,6 +63,7 @@ func printUsage() {
6163
fmt.Println(" fm|findmsm search for measurements")
6264
fmt.Println(" result download results")
6365
fmt.Println(" status measurement status check")
66+
fmt.Println(" measure start new measurement(s)")
6467
fmt.Println("")
6568
fmt.Println("Options:")
6669
flag.PrintDefaults()

config.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,17 @@ var (
3232
flagsFindMsm *flag.FlagSet
3333
flagsGetResult *flag.FlagSet
3434
flagsStatusCheck *flag.FlagSet
35+
flagsMeasure *flag.FlagSet
3536

3637
apiKey *uuid.UUID // specified on the command line explicitly or via env
3738
apiKeys map[string]uuid.UUID // collected from config file
39+
40+
probeSpecCc string
41+
probeSpecArea string
42+
probeSpecAsn string
43+
probeSpecPrefix string
44+
probeSpecList string
45+
probeSpecReuse string
3846
)
3947

4048
const version = "v0.3.0+"
@@ -89,6 +97,7 @@ func configure() {
8997
flagsFindMsm = flag.NewFlagSet("measurement", flag.ExitOnError)
9098
flagsGetResult = flag.NewFlagSet("result", flag.ExitOnError)
9199
flagsStatusCheck = flag.NewFlagSet("status", flag.ExitOnError)
100+
flagsMeasure = flag.NewFlagSet("measure", flag.ExitOnError)
92101

93102
Subcommands = map[string]*flag.FlagSet{
94103
flagsVersion.Name(): flagsVersion,
@@ -97,6 +106,7 @@ func configure() {
97106
flagsFindMsm.Name(): flagsFindMsm,
98107
flagsGetResult.Name(): flagsGetResult,
99108
flagsStatusCheck.Name(): flagsStatusCheck,
109+
flagsMeasure.Name(): flagsStatusCheck,
100110
}
101111
setupFlags()
102112

@@ -134,6 +144,7 @@ func readConfig(confFile string) bool {
134144

135145
// record stuff that was in the config file
136146
loadApiKey(cfg, "list_measurements")
147+
loadApiKey(cfg, "create_measurements")
137148
// TODO: add more API key variations here
138149

139150
// allow config to override where the API is
@@ -156,6 +167,14 @@ func readConfig(confFile string) bool {
156167
// we deliberately ignore errors on creating this dir as it may exist
157168
_ = os.MkdirAll(CacheDir, os.FileMode(0755))
158169

170+
// load probe specification defaults
171+
probeSpecCc = cfg.Section("probespec").Key("probecc").MustString("")
172+
probeSpecArea = cfg.Section("probespec").Key("probearea").MustString("")
173+
probeSpecAsn = cfg.Section("probespec").Key("probeasn").MustString("")
174+
probeSpecPrefix = cfg.Section("probespec").Key("probeprefix").MustString("")
175+
probeSpecList = cfg.Section("probespec").Key("probelist").MustString("")
176+
probeSpecReuse = cfg.Section("probespec").Key("probereuse").MustString("")
177+
159178
return true
160179
}
161180

@@ -192,6 +211,18 @@ cachedir = ""
192211
193212
# List your measurements
194213
list_measurements = ""
214+
215+
# Create new measurement(s)
216+
create_measurements = ""
217+
218+
# default probe specifications for new measurements
219+
[probespec]
220+
probecc = ""
221+
probearea = ""
222+
probeasn = ""
223+
probeprefix = ""
224+
probelist = ""
225+
probereuse = ""
195226
`)
196227

197228
if flagVerbose {
@@ -232,3 +263,22 @@ func getApiKey(function string) *uuid.UUID {
232263

233264
return nil
234265
}
266+
267+
func getProbeSpecDefault(spec string) string {
268+
switch spec {
269+
case "cc":
270+
return probeSpecCc
271+
case "area":
272+
return probeSpecArea
273+
case "asn":
274+
return probeSpecAsn
275+
case "prefix":
276+
return probeSpecPrefix
277+
case "list":
278+
return probeSpecList
279+
case "reuse":
280+
return probeSpecReuse
281+
default:
282+
return ""
283+
}
284+
}

findanchor.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func commandFindAnchor(args []string) {
4343

4444
// counting only
4545
if _, ok := options["count"]; ok {
46-
count, err := filter.GetAnchorCount(flagVerbose)
46+
count, err := filter.GetAnchorCount()
4747
if err != nil {
4848
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
4949
os.Exit(1)
@@ -54,7 +54,7 @@ func commandFindAnchor(args []string) {
5454

5555
// most of the work is done by goatAPI
5656
anchors := make(chan goatapi.AsyncAnchorResult)
57-
go filter.GetAnchors(flagVerbose, anchors)
57+
go filter.GetAnchors(anchors)
5858

5959
// produce output; exact format depends on the "format" option
6060
output.Setup(formatter, flagVerbose, flags.outopts)
@@ -78,6 +78,7 @@ func processFindAnchorFlags(flags *findAnchorFlags) (
7878
) {
7979
options = make(map[string]any)
8080
filter = goatapi.NewAnchorFilter()
81+
filter.Verbose(flagVerbose)
8182

8283
// options
8384

findmsm.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func commandFindMsm(args []string) {
7979

8080
// counting only
8181
if _, ok := options["count"]; ok {
82-
count, err := filter.GetMeasurementCount(flagVerbose)
82+
count, err := filter.GetMeasurementCount()
8383
if err != nil {
8484
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
8585
os.Exit(1)
@@ -90,7 +90,7 @@ func commandFindMsm(args []string) {
9090

9191
// most of the work is done by goatAPI
9292
measurements := make(chan goatapi.AsyncMeasurementResult)
93-
go filter.GetMeasurements(flagVerbose, measurements)
93+
go filter.GetMeasurements(measurements)
9494

9595
// produce output; exact format depends on the "format" option
9696
output.Setup(formatter, flagVerbose, []string(flags.outopts))
@@ -114,6 +114,7 @@ func parseFindMsmFlags(flags *findMsmFlags) (
114114
) {
115115
options = make(map[string]any)
116116
filter = goatapi.NewMeasurementFilter()
117+
filter.Verbose(flagVerbose)
117118

118119
// options
119120

findprobe.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func commandFindProbe(args []string) {
7373

7474
// counting only
7575
if _, ok := options["count"]; ok {
76-
count, err := filter.GetProbeCount(flagVerbose)
76+
count, err := filter.GetProbeCount()
7777
if err != nil {
7878
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
7979
os.Exit(1)
@@ -84,7 +84,7 @@ func commandFindProbe(args []string) {
8484

8585
// most of the work is done by goatAPI
8686
probes := make(chan goatapi.AsyncProbeResult)
87-
go filter.GetProbes(flagVerbose, probes)
87+
go filter.GetProbes(probes)
8888

8989
// produce output; exact format depends on the "format" option
9090
output.Setup(formatter, flagVerbose, flags.outopts)
@@ -108,6 +108,7 @@ func parseFindProbeFlags(flags *findProbeFlags) (
108108
) {
109109
options = make(map[string]any)
110110
filter = goatapi.NewProbeFilter()
111+
filter.Verbose(flagVerbose)
111112

112113
// options
113114

0 commit comments

Comments
 (0)