Alert rules
Three monitors on one service: an alert on the crossing pages once while the disk fills for three days, a spike falls between two reads of a gauge, and minutes with no sample are recorded as zero.
Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.
About
Three monitors on one service, each right about what it measures, and what each one does not report.
main.eml- the menu, the monitors, the disk alert, a spike between reads, the minutes an exporter was down, and a read interval of your ownalerts.eml- free space over three days, the pages each rule sends, the load second by second, which reads land in a spike, and the load minute by minute with the exporter's outage
How each part works:
- As in the corpus case
the-alert-fired-once-and-the-condition-held-for-days, the disk alert holds below 15.0% free, and free space falls from 14.0% to 3.0% over 72 hours, 864 evaluations. On the crossing only: 1 page. On every evaluation: 864. On the crossing and every 4 hours after: 18. The edge rule did what it was written to do, and nothing it sends says the disk went on filling. - As in
the-spike-fit-between-two-samples, the load is read once a minute, and a 20-second spike to 2,000 requests a second starts at 1815 s, between the reads at 1800 and 1860: every read shows 300, and the service drops 20,000 requests over its capacity of 1,000. A read of the highest value since the last read reports the 2,000. - As in
the-missing-metric-was-read-as-zero, the exporter is down for minutes 30 to 47 while the real load is 1,400, over the threshold of 500; the 18 minutes are recorded as 0, and the alert stays quiet. - For a read every n seconds, a spike of n seconds or more always holds a read, and a shorter one can fall between two.
What is checked: menu choices 1 to 6; a read every 1 to 600 seconds, a spike starting at second 0 to 3599 and lasting 1 to 600 seconds. An empty answer cancels.
Sessions: sessions/basic.in gives: - the monitors, the disk alert, the spike and the missing minutes; - your own: reads every 60 s and every 15 s around the spike, and a 60-second spike from 1790 s.
sessions/bad-input.in gives: - menu choices 0 and x; - intervals: a cancel, 0, 601 and x; starts: a cancel, 3600 and x; lengths: a cancel, 0, 601 and x; - a read every second, and every 300 s with a 299-second spike.
Built on the verified corpus cases the-alert-fired-once-and-the-condition-held-for-days (a disk alert that fires on the crossing), the-spike-fit-between-two-samples (a gauge sampled every 60 seconds and a 20-second spike) and the-missing-metric-was-read-as-zero (an exporter outage recorded as zero).
Recorded sessions
What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.
bad-input
interpreter: byte-equal== Alert rules ==
Three monitors, each right about what it measures, and what each one does not report.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 0
Pick a number from 1 to 6.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> x
Pick a number from 1 to 6.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)>
Cancelled.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 0
Type a whole number from 1 to 600.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 601
Type a whole number from 1 to 600.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> x
Type a whole number from 1 to 600.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 30
spike starts at second (0 to 3599)>
Cancelled.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 30
spike starts at second (0 to 3599)> 3600
Type a whole number from 0 to 3599.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 30
spike starts at second (0 to 3599)> x
Type a whole number from 0 to 3599.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 30
spike starts at second (0 to 3599)> 100
spike lasts seconds (1 to 600)>
Cancelled.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 30
spike starts at second (0 to 3599)> 100
spike lasts seconds (1 to 600)> 0
Type a whole number from 1 to 600.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 30
spike starts at second (0 to 3599)> 100
spike lasts seconds (1 to 600)> 601
Type a whole number from 1 to 600.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 30
spike starts at second (0 to 3599)> 100
spike lasts seconds (1 to 600)> x
Type a whole number from 1 to 600.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 1
spike starts at second (0 to 3599)> 100
spike lasts seconds (1 to 600)> 3
Reads every 1 s: the spike from 100 s for 3 s holds 3 reads, from 100 s.
With a read every second, every spike holds a read.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 300
spike starts at second (0 to 3599)> 3550
spike lasts seconds (1 to 600)> 299
Reads every 300 s: the spike from 3550 s for 299 s holds 1 read, at 3600 s.
A spike of 300 seconds or more always holds a read; a shorter one can fall between two.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 6
Bye.
What was typed (44 lines)
0
x
5
5
0
5
601
5
x
5
30
5
30
3600
5
30
x
5
30
100
5
30
100
0
5
30
100
601
5
30
100
x
5
1
100
3
5
300
3550
299
6
basic
interpreter: byte-equal== Alert rules ==
Three monitors, each right about what it measures, and what each one does not report.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 1
Three monitors on one service:
disk free space, read every 5 minutes; the alert holds below 15.0%
load requests a second, read once a minute; the alert fires over 500, and the service drops what is over 1,000
exporter the load reaches the monitor through an exporter; a minute without a sample is recorded as 0
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 2
Free space after it crossed below 15.0%, while a cleanup fails to keep up:
hour free
0 14.0%
12 12.2%
24 10.4%
36 8.5%
48 6.7%
60 4.9%
72 3.0%
The condition held for all 72 hours, 864 evaluations. Pages sent:
on the crossing only 1 page
on every evaluation 864 pages
on the crossing, then every 4 hours 18 pages
The edge rule did what it was written to do: one page. Nothing it sends says that the disk went on filling for three days.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 3
The hour's load, read once a minute, around the half hour:
read at requests a second
1740 s 300
1800 s 300
1860 s 300
1920 s 300
The highest of the 60 reads: 300. The highest second of the hour: 2,000, from 1815 s for 20 seconds, between the reads at 1800 and 1860.
Over capacity for those 20 seconds, the service dropped 20,000 requests, and no read was over the threshold.
Read as the highest value since the last read, the 1860 s read reports 2,000.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 4
The last hour, minute by minute: 42 minutes with a sample and 18 without, minutes 30 to 47, recorded as 0.
minutes real load recorded
0-29 300 300
30-47 1,400 0
48-59 300 300
The highest recorded minute: 300, under the threshold of 500, so the alert stayed quiet. The real load was 1,400 for those 18 minutes, 900 over it.
Recorded as missing rather than as 0, the same minutes raise a no-data alert at minute 30.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 60
spike starts at second (0 to 3599)> 1815
spike lasts seconds (1 to 600)> 20
Reads every 60 s: the spike from 1815 s for 20 s holds no read, and the gauge never sees it.
A spike of 60 seconds or more always holds a read; a shorter one can fall between two.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 15
spike starts at second (0 to 3599)> 1815
spike lasts seconds (1 to 600)> 20
Reads every 15 s: the spike from 1815 s for 20 s holds 2 reads, from 1815 s.
A spike of 15 seconds or more always holds a read; a shorter one can fall between two.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 5
read every how many seconds (1 to 600)> 60
spike starts at second (0 to 3599)> 1790
spike lasts seconds (1 to 600)> 60
Reads every 60 s: the spike from 1790 s for 60 s holds 1 read, at 1800 s.
A spike of 60 seconds or more always holds a read; a shorter one can fall between two.
1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit
choice> 6
Bye.
What was typed (17 lines)
1
2
3
4
5
60
1815
20
5
15
1815
20
5
60
1790
60
6
Modules
The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.
main.eml(entry)
eml# P171 alert rules: three monitors on one service - the disk alert over
# three days, a spike between two reads of a gauge, the minutes an exporter
# was down, and a read interval of your own.
import alerts
def plain(s):
0 => i
len(s) => j
while i < j and s[i] == " ":
i + 1 => i
while j > i and s[j - 1] == " ":
j - 1 => j
return s[i:j]
def pad(s, n):
s => out
while len(out) < n:
out + " " => out
return out
def lpad(s, n):
s => out
while len(out) < n:
" " + out => out
return out
def digits(t):
for c in t:
if c < "0" or c > "9":
return False
return len(t) > 0
def commas(n):
str(n) => s
"" => out
0 => k
len(s) => i
while i > 0:
if k == 3:
"," + out => out
0 => k
s[i - 1] + out => out
k + 1 => k
i - 1 => i
return out
def pct(t):
# tenths of a percent
return str(int((t - t % 10) / 10)) + "." + str(t % 10) + "%"
def plural(n, word):
if n == 1:
return "1 " + word
return commas(n) + " " + word + "s"
def show_monitors():
"Three monitors on one service:" ^0
(" " + pad("disk", 10) + "free space, read every 5 minutes; the alert holds below " + pct(alerts.disk_threshold())) ^0
(" " + pad("load", 10) + "requests a second, read once a minute; the alert fires over " + commas(alerts.load_threshold()) + ", and the service drops what is over " + commas(alerts.capacity())) ^0
(" " + pad("exporter", 10) + "the load reaches the monitor through an exporter; a minute without a sample is recorded as 0") ^0
def show_disk():
"Free space after it crossed below 15.0%, while a cleanup fails to keep up:" ^0
(" " + pad("hour", 7) + "free") ^0
0 => h
while h <= 72:
(" " + pad(str(h), 7) + pct(alerts.free(h))) ^0
h + 12 => h
("The condition held for all 72 hours, " + commas(alerts.evaluations()) + " evaluations. Pages sent:") ^0
(" " + pad("on the crossing only", 38) + lpad(plural(alerts.pages("edge", 4), "page"), 10)) ^0
(" " + pad("on every evaluation", 38) + lpad(plural(alerts.pages("level", 4), "page"), 10)) ^0
(" " + pad("on the crossing, then every 4 hours", 38) + lpad(plural(alerts.pages("reminder", 4), "page"), 10)) ^0
"The edge rule did what it was written to do: one page. Nothing it sends says that the disk went on filling for three days." ^0
def show_spike():
"The hour's load, read once a minute, around the half hour:" ^0
(" " + pad("read at", 10) + "requests a second") ^0
0 => most
0 => s
while s < 3600:
if alerts.load(s) > most:
alerts.load(s) => most
if s >= 1740 and s <= 1920:
(" " + pad(str(s) + " s", 10) + commas(alerts.load(s))) ^0
s + 60 => s
0 => peak
0 => dropped
0 => t
while t < 3600:
if alerts.load(t) > peak:
alerts.load(t) => peak
if alerts.load(t) > alerts.capacity():
dropped + alerts.load(t) - alerts.capacity() => dropped
t + 1 => t
("The highest of the 60 reads: " + commas(most) + ". The highest second of the hour: " + commas(peak) + ", from 1815 s for 20 seconds, between the reads at 1800 and 1860.") ^0
("Over capacity for those 20 seconds, the service dropped " + commas(dropped) + " requests, and no read was over the threshold.") ^0
("Read as the highest value since the last read, the 1860 s read reports " + commas(peak) + ".") ^0
def show_missing():
0 => up
0 => recorded_max
0 => m
while m < 60:
if alerts.exporter_up(m):
up + 1 => up
if alerts.minute_load(m) > recorded_max:
alerts.minute_load(m) => recorded_max
m + 1 => m
("The last hour, minute by minute: " + str(up) + " minutes with a sample and " + str(60 - up) + " without, minutes 30 to 47, recorded as 0.") ^0
(" " + pad("minutes", 10) + lpad("real load", 10) + lpad("recorded", 10)) ^0
(" " + pad("0-29", 10) + lpad(commas(alerts.minute_load(0)), 10) + lpad(commas(alerts.minute_load(0)), 10)) ^0
(" " + pad("30-47", 10) + lpad(commas(alerts.minute_load(30)), 10) + lpad("0", 10)) ^0
(" " + pad("48-59", 10) + lpad(commas(alerts.minute_load(48)), 10) + lpad(commas(alerts.minute_load(48)), 10)) ^0
("The highest recorded minute: " + commas(recorded_max) + ", under the threshold of " + commas(alerts.load_threshold()) + ", so the alert stayed quiet. The real load was " + commas(alerts.minute_load(30)) + " for those 18 minutes, " + commas(alerts.minute_load(30) - alerts.load_threshold()) + " over it.") ^0
"Recorded as missing rather than as 0, the same minutes raise a no-data alert at minute 30." ^0
def number(prompt, lo, hi):
# the whole number typed, or -1 after a cancel or a message
plain(input(prompt + " (" + str(lo) + " to " + str(hi) + ")> ")) => t
if t == "":
"Cancelled." ^0
return 0 - 1
if len(t) > len(str(hi)) or not digits(t) or int(t) < lo or int(t) > hi:
("Type a whole number from " + str(lo) + " to " + str(hi) + ".") ^0
return 0 - 1
return int(t)
def own():
number("read every how many seconds", 1, 600) => every
if every < 0:
return
number("spike starts at second", 0, 3599) => start
if start < 0:
return
number("spike lasts seconds", 1, 600) => length
if length < 0:
return
alerts.samples_in(start, length, every) => got
if len(got) == 0:
("Reads every " + str(every) + " s: the spike from " + str(start) + " s for " + str(length) + " s holds no read, and the gauge never sees it.") ^0
elif len(got) == 1:
("Reads every " + str(every) + " s: the spike from " + str(start) + " s for " + str(length) + " s holds 1 read, at " + str(got[0]) + " s.") ^0
else:
("Reads every " + str(every) + " s: the spike from " + str(start) + " s for " + str(length) + " s holds " + str(len(got)) + " reads, from " + str(got[0]) + " s.") ^0
if every == 1:
"With a read every second, every spike holds a read." ^0
else:
("A spike of " + str(every) + " seconds or more always holds a read; a shorter one can fall between two.") ^0
"== Alert rules ==" ^0
"Three monitors, each right about what it measures, and what each one does not report." ^0
True => running
while running:
"" ^0
"1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show_monitors()
elif choice == "2":
show_disk()
elif choice == "3":
show_spike()
elif choice == "4":
show_missing()
elif choice == "5":
own()
elif choice == "6":
False => running
else:
"Pick a number from 1 to 6." ^0
"Bye." ^0
Python projection (main.py)
import alerts
def plain(s):
i = 0
j = len(s)
while i < j and s[i] == " ":
i = i + 1
while j > i and s[j - 1] == " ":
j = j - 1
return s[i:j]
def pad(s, n):
out = s
while len(out) < n:
out = out + " "
return out
def lpad(s, n):
out = s
while len(out) < n:
out = " " + out
return out
def digits(t):
for c in t:
if c < "0" or c > "9":
return False
return len(t) > 0
def commas(n):
s = str(n)
out = ""
k = 0
i = len(s)
while i > 0:
if k == 3:
out = "," + out
k = 0
out = s[i - 1] + out
k = k + 1
i = i - 1
return out
def pct(t):
return str(int((t - t % 10) / 10)) + "." + str(t % 10) + "%"
def plural(n, word):
if n == 1:
return "1 " + word
return commas(n) + " " + word + "s"
def show_monitors():
print("Three monitors on one service:")
print(" " + pad("disk", 10) + "free space, read every 5 minutes; the alert holds below " + pct(alerts.disk_threshold()))
print(" " + pad("load", 10) + "requests a second, read once a minute; the alert fires over " + commas(alerts.load_threshold()) + ", and the service drops what is over " + commas(alerts.capacity()))
print(" " + pad("exporter", 10) + "the load reaches the monitor through an exporter; a minute without a sample is recorded as 0")
def show_disk():
print("Free space after it crossed below 15.0%, while a cleanup fails to keep up:")
print(" " + pad("hour", 7) + "free")
h = 0
while h <= 72:
print(" " + pad(str(h), 7) + pct(alerts.free(h)))
h = h + 12
print("The condition held for all 72 hours, " + commas(alerts.evaluations()) + " evaluations. Pages sent:")
print(" " + pad("on the crossing only", 38) + lpad(plural(alerts.pages("edge", 4), "page"), 10))
print(" " + pad("on every evaluation", 38) + lpad(plural(alerts.pages("level", 4), "page"), 10))
print(" " + pad("on the crossing, then every 4 hours", 38) + lpad(plural(alerts.pages("reminder", 4), "page"), 10))
print("The edge rule did what it was written to do: one page. Nothing it sends says that the disk went on filling for three days.")
def show_spike():
print("The hour's load, read once a minute, around the half hour:")
print(" " + pad("read at", 10) + "requests a second")
most = 0
s = 0
while s < 3600:
if alerts.load(s) > most:
most = alerts.load(s)
if s >= 1740 and s <= 1920:
print(" " + pad(str(s) + " s", 10) + commas(alerts.load(s)))
s = s + 60
peak = 0
dropped = 0
t = 0
while t < 3600:
if alerts.load(t) > peak:
peak = alerts.load(t)
if alerts.load(t) > alerts.capacity():
dropped = dropped + alerts.load(t) - alerts.capacity()
t = t + 1
print("The highest of the 60 reads: " + commas(most) + ". The highest second of the hour: " + commas(peak) + ", from 1815 s for 20 seconds, between the reads at 1800 and 1860.")
print("Over capacity for those 20 seconds, the service dropped " + commas(dropped) + " requests, and no read was over the threshold.")
print("Read as the highest value since the last read, the 1860 s read reports " + commas(peak) + ".")
def show_missing():
up = 0
recorded_max = 0
m = 0
while m < 60:
if alerts.exporter_up(m):
up = up + 1
if alerts.minute_load(m) > recorded_max:
recorded_max = alerts.minute_load(m)
m = m + 1
print("The last hour, minute by minute: " + str(up) + " minutes with a sample and " + str(60 - up) + " without, minutes 30 to 47, recorded as 0.")
print(" " + pad("minutes", 10) + lpad("real load", 10) + lpad("recorded", 10))
print(" " + pad("0-29", 10) + lpad(commas(alerts.minute_load(0)), 10) + lpad(commas(alerts.minute_load(0)), 10))
print(" " + pad("30-47", 10) + lpad(commas(alerts.minute_load(30)), 10) + lpad("0", 10))
print(" " + pad("48-59", 10) + lpad(commas(alerts.minute_load(48)), 10) + lpad(commas(alerts.minute_load(48)), 10))
print("The highest recorded minute: " + commas(recorded_max) + ", under the threshold of " + commas(alerts.load_threshold()) + ", so the alert stayed quiet. The real load was " + commas(alerts.minute_load(30)) + " for those 18 minutes, " + commas(alerts.minute_load(30) - alerts.load_threshold()) + " over it.")
print("Recorded as missing rather than as 0, the same minutes raise a no-data alert at minute 30.")
def number(prompt, lo, hi):
t = plain(input(prompt + " (" + str(lo) + " to " + str(hi) + ")> "))
if t == "":
print("Cancelled.")
return 0 - 1
if len(t) > len(str(hi)) or not digits(t) or int(t) < lo or int(t) > hi:
print("Type a whole number from " + str(lo) + " to " + str(hi) + ".")
return 0 - 1
return int(t)
def own():
every = number("read every how many seconds", 1, 600)
if every < 0:
return
start = number("spike starts at second", 0, 3599)
if start < 0:
return
length = number("spike lasts seconds", 1, 600)
if length < 0:
return
got = alerts.samples_in(start, length, every)
if len(got) == 0:
print("Reads every " + str(every) + " s: the spike from " + str(start) + " s for " + str(length) + " s holds no read, and the gauge never sees it.")
elif len(got) == 1:
print("Reads every " + str(every) + " s: the spike from " + str(start) + " s for " + str(length) + " s holds 1 read, at " + str(got[0]) + " s.")
else:
print("Reads every " + str(every) + " s: the spike from " + str(start) + " s for " + str(length) + " s holds " + str(len(got)) + " reads, from " + str(got[0]) + " s.")
if every == 1:
print("With a read every second, every spike holds a read.")
else:
print("A spike of " + str(every) + " seconds or more always holds a read; a shorter one can fall between two.")
print("== Alert rules ==")
print("Three monitors, each right about what it measures, and what each one does not report.")
running = True
while running:
print("")
print("1) the monitors 2) the disk alert 3) a spike between reads 4) missing minutes 5) a read interval of your own 6) quit")
choice = plain(input("choice> "))
if choice == "1":
show_monitors()
elif choice == "2":
show_disk()
elif choice == "3":
show_spike()
elif choice == "4":
show_missing()
elif choice == "5":
own()
elif choice == "6":
running = False
else:
print("Pick a number from 1 to 6.")
print("Bye.")
alerts.eml
eml# P171 alert rules - three monitors, each correct on its own terms. As in
# the corpus case the-alert-fired-once-and-the-condition-held-for-days, an
# alert on the crossing pages once, and the condition it reports goes on
# for three days. As in the-spike-fit-between-two-samples, a gauge read once
# a minute never sees a spike that starts and ends between two reads. And as
# in the-missing-metric-was-read-as-zero, a minute with no sample recorded
# as zero is a minute under every threshold.
def disk_threshold():
# free space, in tenths of a percent, below which the disk alert holds
return 150
def free(h):
# free space at hour h of the three days, in tenths of a percent
return 140 - int(110 * h / 72)
def evaluations():
# the rule is evaluated every 5 minutes for the 72 hours
return 72 * 12
def pages(rule, every):
# pages sent over the 72 hours: "edge" on the crossing only, "level" on
# every evaluation that finds the condition, "reminder" on the crossing
# and again every `every` hours while it holds
0 => n
0 => k
while k < evaluations():
int((k - k % 12) / 12) => h
if free(h) < disk_threshold():
if rule == "level":
n + 1 => n
elif k == 0:
n + 1 => n
elif rule == "reminder" and k % (12 * every) == 0:
n + 1 => n
k + 1 => k
return n
def load(s):
# requests a second at second s of the hour: a 20-second spike at 1815
if s >= 1815 and s < 1835:
return 2000
return 300
def capacity():
return 1000
def samples_in(start, length, every):
# the reads, one every `every` seconds from second 0, that land in the
# seconds start .. start + length - 1
[] => out
start + every - 1 => first
first - first % every => s
while s < start + length:
out + [s] => out
s + every => s
return out
def load_threshold():
return 500
def minute_load(m):
# the real requests a second in minute m; the exporter is down 30 - 47
if m >= 30 and m < 48:
return 1400
return 300
def exporter_up(m):
return m < 30 or m >= 48
Python projection (alerts.py)
def disk_threshold():
return 150
def free(h):
return 140 - int(110 * h / 72)
def evaluations():
return 72 * 12
def pages(rule, every):
n = 0
k = 0
while k < evaluations():
h = int((k - k % 12) / 12)
if free(h) < disk_threshold():
if rule == "level":
n = n + 1
elif k == 0:
n = n + 1
elif rule == "reminder" and k % (12 * every) == 0:
n = n + 1
k = k + 1
return n
def load(s):
if s >= 1815 and s < 1835:
return 2000
return 300
def capacity():
return 1000
def samples_in(start, length, every):
out = []
first = start + every - 1
s = first - first % every
while s < start + length:
out = out + [s]
s = s + every
return out
def load_threshold():
return 500
def minute_load(m):
if m >= 30 and m < 48:
return 1400
return 300
def exporter_up(m):
return m < 30 or m >= 48