[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [metrics-web/master] Provide advertised bandwidth distribution stats.
commit 7509b48d04733c8ab4e3163a871eb6459de1c956
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Fri Jan 31 13:39:48 2014 +0100
Provide advertised bandwidth distribution stats.
Implements #10460.
---
modules/advbwdist/.gitignore | 4 +
modules/advbwdist/aggregate.R | 16 +++
modules/advbwdist/build.xml | 44 ++++++
.../src/org/torproject/metrics/advbwdist/Main.java | 152 ++++++++++++++++++++
shared/bin/60-run-advbwdist-stats.sh | 6 +
shared/bin/99-copy-stats-files.sh | 1 +
website/etc/web.xml | 6 +
website/rserve/graphs.R | 54 +++++++
.../metrics/web/graphs/GraphParameterChecker.java | 40 ++++++
.../metrics/web/graphs/RObjectGenerator.java | 3 +
.../metrics/web/research/ResearchStatsServlet.java | 1 +
website/web/WEB-INF/network.jsp | 104 ++++++++++++++
website/web/WEB-INF/stats.jsp | 29 ++++
13 files changed, 460 insertions(+)
diff --git a/modules/advbwdist/.gitignore b/modules/advbwdist/.gitignore
new file mode 100644
index 0000000..4bb76a5
--- /dev/null
+++ b/modules/advbwdist/.gitignore
@@ -0,0 +1,4 @@
+classes/
+stats/
+status/
+
diff --git a/modules/advbwdist/aggregate.R b/modules/advbwdist/aggregate.R
new file mode 100644
index 0000000..ee52a64
--- /dev/null
+++ b/modules/advbwdist/aggregate.R
@@ -0,0 +1,16 @@
+require(reshape)
+t <- read.csv("stats/advbwdist-validafter.csv", stringsAsFactors = FALSE)
+t <- t[t$valid_after < paste(Sys.Date() - 1, "23:59:59"), ]
+t <- aggregate(list(advbw = as.numeric(t$advbw)),
+ by = list(date = as.Date(cut.Date(as.Date(t$valid_after), "day")),
+ isexit = !is.na(t$isexit), relay = ifelse(is.na(t$relay), -1, t$relay),
+ percentile = ifelse(is.na(t$percentile), -1, t$percentile)),
+ FUN = median)
+t <- data.frame(date = t$date, isexit = ifelse(t$isexit, "t", ""),
+ relay = ifelse(t$relay < 0, NA, t$relay),
+ percentile = ifelse(t$percentile < 0, NA, t$percentile),
+ advbw = floor(t$advbw))
+t <- t[order(t$date, t$isexit, t$relay, t$percentile), ]
+write.csv(t, "stats/advbwdist.csv", quote = FALSE, row.names = FALSE,
+ na = "")
+
diff --git a/modules/advbwdist/build.xml b/modules/advbwdist/build.xml
new file mode 100644
index 0000000..16845ee
--- /dev/null
+++ b/modules/advbwdist/build.xml
@@ -0,0 +1,44 @@
+<project default="run" name="advbwdist" basedir=".">
+
+ <property name="sources" value="src"/>
+ <property name="classes" value="classes"/>
+ <path id="classpath">
+ <pathelement path="${classes}"/>
+ <fileset dir="/usr/share/java">
+ <include name="commons-codec.jar"/>
+ <include name="commons-compress.jar"/>
+ <include name="commons-lang.jar"/>
+ </fileset>
+ <fileset dir="../../deps/metrics-lib">
+ <include name="descriptor.jar"/>
+ </fileset>
+ </path>
+
+ <target name="metrics-lib">
+ <ant dir="../../deps/metrics-lib"/>
+ </target>
+
+ <target name="compile" depends="metrics-lib">
+ <mkdir dir="${classes}"/>
+ <javac destdir="${classes}"
+ srcdir="${sources}"
+ source="1.5"
+ target="1.5"
+ debug="true"
+ deprecation="true"
+ optimize="false"
+ failonerror="true"
+ includeantruntime="false">
+ <classpath refid="classpath"/>
+ </javac>
+ </target>
+
+ <target name="run" depends="compile">
+ <java fork="true"
+ maxmemory="1024m"
+ classname="org.torproject.metrics.advbwdist.Main">
+ <classpath refid="classpath"/>
+ </java>
+ </target>
+</project>
+
diff --git a/modules/advbwdist/src/org/torproject/metrics/advbwdist/Main.java b/modules/advbwdist/src/org/torproject/metrics/advbwdist/Main.java
new file mode 100644
index 0000000..09eaab0
--- /dev/null
+++ b/modules/advbwdist/src/org/torproject/metrics/advbwdist/Main.java
@@ -0,0 +1,152 @@
+package org.torproject.metrics.advbwdist;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
+import org.torproject.descriptor.DescriptorReader;
+import org.torproject.descriptor.DescriptorSourceFactory;
+import org.torproject.descriptor.NetworkStatusEntry;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+import org.torproject.descriptor.ServerDescriptor;
+
+public class Main {
+ public static void main(String[] args) throws IOException {
+
+ /* Parse server descriptors, not keeping a parse history, and memorize
+ * the advertised bandwidth for every server descriptor. */
+ DescriptorReader descriptorReader =
+ DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addDirectory(
+ new File("../../shared/in/relay-descriptors/server-descriptors"));
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ Map<String, Long> serverDescriptors =
+ new HashMap<String, Long>();
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof ServerDescriptor)) {
+ continue;
+ }
+ ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor;
+ String digest = serverDescriptor.getServerDescriptorDigest();
+ long advertisedBandwidth = Math.min(Math.min(
+ serverDescriptor.getBandwidthRate(),
+ serverDescriptor.getBandwidthBurst()),
+ serverDescriptor.getBandwidthObserved());
+ serverDescriptors.put(digest.toUpperCase(), advertisedBandwidth);
+ }
+ }
+
+ /* Parse consensuses, keeping a parse history. */
+ descriptorReader = DescriptorSourceFactory.createDescriptorReader();
+ descriptorReader.addDirectory(
+ new File("../../shared/in/relay-descriptors/consensuses"));
+ descriptorReader.setExcludeFiles(
+ new File("status/parsed-consensuses"));
+ descriptorFiles = descriptorReader.readDescriptors();
+ File resultsFile = new File("stats/advbwdist-validafter.csv");
+ resultsFile.getParentFile().mkdirs();
+ boolean writeHeader = !resultsFile.exists();
+ BufferedWriter bw = new BufferedWriter(new FileWriter(resultsFile,
+ true));
+ if (writeHeader) {
+ bw.write("valid_after,isexit,relay,percentile,advbw\n");
+ }
+ SimpleDateFormat dateTimeFormat = new SimpleDateFormat(
+ "yyyy-MM-dd HH:mm:ss");
+ dateTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof RelayNetworkStatusConsensus)) {
+ continue;
+ }
+
+ /* Parse server descriptor digests from consensus and look up
+ * advertised bandwidths. */
+ RelayNetworkStatusConsensus consensus =
+ (RelayNetworkStatusConsensus) descriptor;
+ String validAfter = dateTimeFormat.format(
+ consensus.getValidAfterMillis());
+ List<Long> advertisedBandwidthsAllRelays = new ArrayList<Long>(),
+ advertisedBandwidthsExitsOnly = new ArrayList<Long>();
+ for (NetworkStatusEntry relay :
+ consensus.getStatusEntries().values()) {
+ if (!relay.getFlags().contains("Running")) {
+ continue;
+ }
+ String serverDescriptorDigest = relay.getDescriptor().
+ toUpperCase();
+ if (!serverDescriptors.containsKey(serverDescriptorDigest)) {
+ continue;
+ }
+ long advertisedBandwidth = serverDescriptors.get(
+ serverDescriptorDigest);
+ advertisedBandwidthsAllRelays.add(advertisedBandwidth);
+ if (relay.getFlags().contains("Exit") &&
+ !relay.getFlags().contains("BadExit")) {
+ advertisedBandwidthsExitsOnly.add(advertisedBandwidth);
+ }
+ }
+
+ /* Write advertised bandwidths of n-th fastest relays/exits. */
+ Collections.sort(advertisedBandwidthsAllRelays,
+ Collections.reverseOrder());
+ Collections.sort(advertisedBandwidthsExitsOnly,
+ Collections.reverseOrder());
+ int[] fastestRelays = new int[] { 1, 2, 3, 5, 10, 20, 30, 50, 100,
+ 200, 300, 500, 1000, 2000, 3000, 5000 };
+ for (int fastestRelay : fastestRelays) {
+ if (advertisedBandwidthsAllRelays.size() >= fastestRelay) {
+ bw.write(String.format("%s,,%d,,%d%n", validAfter,
+ fastestRelay,
+ advertisedBandwidthsAllRelays.get(fastestRelay - 1)));
+ }
+ }
+ for (int fastestRelay : fastestRelays) {
+ if (advertisedBandwidthsExitsOnly.size() >= fastestRelay) {
+ bw.write(String.format("%s,TRUE,%d,,%d%n", validAfter,
+ fastestRelay,
+ advertisedBandwidthsExitsOnly.get(fastestRelay - 1)));
+ }
+ }
+
+ /* Write advertised bandwidth percentiles of relays/exits. */
+ Collections.sort(advertisedBandwidthsAllRelays);
+ Collections.sort(advertisedBandwidthsExitsOnly);
+ int[] percentiles = new int[] { 0, 1, 2, 3, 5, 9, 10, 20, 25, 30,
+ 40, 50, 60, 70, 75, 80, 90, 91, 95, 97, 98, 99, 100 };
+ if (!advertisedBandwidthsAllRelays.isEmpty()) {
+ for (int percentile : percentiles) {
+ bw.write(String.format("%s,,,%d,%d%n", validAfter,
+ percentile, advertisedBandwidthsAllRelays.get(
+ ((advertisedBandwidthsAllRelays.size() - 1) *
+ percentile) / 100)));
+ }
+ }
+ if (!advertisedBandwidthsExitsOnly.isEmpty()) {
+ for (int percentile : percentiles) {
+ bw.write(String.format("%s,TRUE,,%d,%d%n", validAfter,
+ percentile, advertisedBandwidthsExitsOnly.get(
+ ((advertisedBandwidthsExitsOnly.size() - 1) *
+ percentile) / 100)));
+ }
+ }
+ }
+ }
+ bw.close();
+ }
+}
+
diff --git a/shared/bin/60-run-advbwdist-stats.sh b/shared/bin/60-run-advbwdist-stats.sh
new file mode 100755
index 0000000..a06e848
--- /dev/null
+++ b/shared/bin/60-run-advbwdist-stats.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+cd modules/advbwdist/
+ant | grep "\[java\]"
+R --slave -f aggregate.R
+cd ../../
+
diff --git a/shared/bin/99-copy-stats-files.sh b/shared/bin/99-copy-stats-files.sh
index 6dce205..2292da2 100755
--- a/shared/bin/99-copy-stats-files.sh
+++ b/shared/bin/99-copy-stats-files.sh
@@ -1,4 +1,5 @@
#!/bin/sh
mkdir -p shared/stats
cp -a modules/legacy/stats/*.csv shared/stats/
+cp -a modules/advbwdist/stats/advbwdist.csv shared/stats/
diff --git a/website/etc/web.xml b/website/etc/web.xml
index fa446bc..1a0e372 100644
--- a/website/etc/web.xml
+++ b/website/etc/web.xml
@@ -228,6 +228,12 @@
<url-pattern>/userstats-bridge-version.png</url-pattern>
<url-pattern>/userstats-bridge-version.pdf</url-pattern>
<url-pattern>/userstats-bridge-version.svg</url-pattern>
+ <url-pattern>/advbwdist-perc.png</url-pattern>
+ <url-pattern>/advbwdist-perc.pdf</url-pattern>
+ <url-pattern>/advbwdist-perc.svg</url-pattern>
+ <url-pattern>/advbwdist-relay.png</url-pattern>
+ <url-pattern>/advbwdist-relay.pdf</url-pattern>
+ <url-pattern>/advbwdist-relay.svg</url-pattern>
</servlet-mapping>
<servlet>
diff --git a/website/rserve/graphs.R b/website/rserve/graphs.R
index 614bcea..bdeebe0 100644
--- a/website/rserve/graphs.R
+++ b/website/rserve/graphs.R
@@ -927,3 +927,57 @@ plot_userstats_bridge_version <- function(start, end, version, path) {
plot_userstats(start, end, 'bridge', 'version', version, 'off', path)
}
+plot_advbwdist_perc <- function(start, end, p, path) {
+ end <- min(end, as.character(Sys.Date() - 2))
+ t <- read.csv(paste("/srv/metrics.torproject.org/web/shared/stats/",
+ "advbwdist.csv", sep = ""), stringsAsFactors = FALSE)
+ t <- t[t$date >= start & t$date <= end &
+ t$percentile %in% as.numeric(p), ]
+ t <- data.frame(date = t$date, advbw = t$advbw / 2^20,
+ variable = ifelse(t$isexit != "t", "All relays",
+ "Exits only"),
+ percentile = as.factor(t$percentile))
+ date_breaks <- date_breaks(
+ as.numeric(max(as.Date(t$date, "%Y-%m-%d")) -
+ min(as.Date(t$date, "%Y-%m-%d"))))
+ ggplot(t, aes(x = as.Date(date), y = advbw, colour = percentile)) +
+ facet_grid(variable ~ .) +
+ geom_line(size = 0.75) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = ""),
+ format = date_breaks$format, major = date_breaks$major,
+ minor = date_breaks$minor) +
+ scale_y_continuous(name = "Advertised bandwidth in MiB/s\n",
+ limits = c(0, max(t$advbw, na.rm = TRUE))) +
+ scale_colour_hue(name = "Percentile",
+ breaks = rev(levels(t$percentile))) +
+ opts(title = "Advertised bandwidth distribution\n")
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
+plot_advbwdist_relay <- function(start, end, n, path) {
+ end <- min(end, as.character(Sys.Date() - 2))
+ t <- read.csv(paste("/srv/metrics.torproject.org/web/shared/stats/",
+ "advbwdist.csv", sep = ""), stringsAsFactors = FALSE)
+ t <- t[t$date >= start & t$date <= end & t$relay %in% as.numeric(n), ]
+ t <- data.frame(date = t$date, advbw = t$advbw / 2^20,
+ variable = ifelse(t$isexit != "t", "All relays",
+ "Exits only"),
+ relay = as.factor(t$relay))
+ date_breaks <- date_breaks(
+ as.numeric(max(as.Date(t$date, "%Y-%m-%d")) -
+ min(as.Date(t$date, "%Y-%m-%d"))))
+ ggplot(t, aes(x = as.Date(date), y = advbw, colour = relay)) +
+ facet_grid(variable ~ .) +
+ geom_line(size = 0.75) +
+ scale_x_date(name = paste("\nThe Tor Project - ",
+ "https://metrics.torproject.org/", sep = ""),
+ format = date_breaks$format, major = date_breaks$major,
+ minor = date_breaks$minor) +
+ scale_y_continuous(name = "Advertised bandwidth in MiB/s\n",
+ limits = c(0, max(t$advbw, na.rm = TRUE))) +
+ scale_colour_hue(name = "n", breaks = levels(t$relay)) +
+ opts(title = "Advertised bandwidth of n-th fastest relays\n")
+ ggsave(filename = path, width = 8, height = 5, dpi = 72)
+}
+
diff --git a/website/src/org/torproject/metrics/web/graphs/GraphParameterChecker.java b/website/src/org/torproject/metrics/web/graphs/GraphParameterChecker.java
index 098d908..3e04bea 100644
--- a/website/src/org/torproject/metrics/web/graphs/GraphParameterChecker.java
+++ b/website/src/org/torproject/metrics/web/graphs/GraphParameterChecker.java
@@ -61,6 +61,10 @@ public class GraphParameterChecker {
this.knownParameterValues.put("transport",
"obfs2,obfs3,websocket,<OR>,<??>");
this.knownParameterValues.put("version", "v4,v6");
+ this.knownParameterValues.put("p", "100,99,98,97,95,91,90,80,75,70,"
+ + "60,50,40,30,25,20,10,9,5,3,2,1,0");
+ this.knownParameterValues.put("n", "1,2,3,5,10,20,30,50,100,200,300,"
+ + "500,1000,2000,3000,5000");
}
public void setAvailableGraphs(Map<String, String> availableGraphs) {
@@ -273,6 +277,42 @@ public class GraphParameterChecker {
recognizedGraphParameters.put("version", versionParameters);
}
+ /* Parse p if supported by the graph type. If no p's are passed, use
+ * "100" as default. */
+ if (supportedGraphParameters.contains("p")) {
+ String[] pParameters = (String[]) requestParameters.get("p");
+ if (pParameters != null) {
+ List<String> knownPs = Arrays.asList(
+ this.knownParameterValues.get("p").split(","));
+ for (String p : pParameters) {
+ if (p == null || p.length() == 0 || !knownPs.contains(p)) {
+ return null;
+ }
+ }
+ } else {
+ pParameters = new String[] { "100" };
+ }
+ recognizedGraphParameters.put("p", pParameters);
+ }
+
+ /* Parse n if supported by the graph type. If no n's are passed, use
+ * "1" as default. */
+ if (supportedGraphParameters.contains("n")) {
+ String[] nParameters = (String[]) requestParameters.get("n");
+ if (nParameters != null) {
+ List<String> knownNs = Arrays.asList(
+ this.knownParameterValues.get("n").split(","));
+ for (String n : nParameters) {
+ if (n == null || n.length() == 0 || !knownNs.contains(n)) {
+ return null;
+ }
+ }
+ } else {
+ nParameters = new String[] { "1" };
+ }
+ recognizedGraphParameters.put("n", nParameters);
+ }
+
/* We now have a map with all required graph parameters. Return it. */
return recognizedGraphParameters;
}
diff --git a/website/src/org/torproject/metrics/web/graphs/RObjectGenerator.java b/website/src/org/torproject/metrics/web/graphs/RObjectGenerator.java
index 8b64ff7..314f38a 100644
--- a/website/src/org/torproject/metrics/web/graphs/RObjectGenerator.java
+++ b/website/src/org/torproject/metrics/web/graphs/RObjectGenerator.java
@@ -109,6 +109,9 @@ public class RObjectGenerator implements ServletContextListener {
"start,end,transport,filename");
this.availableGraphs.put("userstats-bridge-version",
"start,end,version,filename");
+ this.availableGraphs.put("advbwdist-perc", "start,end,p,filename");
+ this.availableGraphs.put("advbwdist-relay", "start,end,n,filename");
+
this.availableGraphFileTypes = new HashSet<String>(Arrays.asList(
"png,pdf,svg".split(",")));
GraphParameterChecker.getInstance().setAvailableGraphs(
diff --git a/website/src/org/torproject/metrics/web/research/ResearchStatsServlet.java b/website/src/org/torproject/metrics/web/research/ResearchStatsServlet.java
index e9eaa38..d1f9cfb 100644
--- a/website/src/org/torproject/metrics/web/research/ResearchStatsServlet.java
+++ b/website/src/org/torproject/metrics/web/research/ResearchStatsServlet.java
@@ -34,6 +34,7 @@ public class ResearchStatsServlet extends HttpServlet {
this.availableStatisticsFiles.add("clients");
this.availableStatisticsFiles.add("torperf");
this.availableStatisticsFiles.add("connbidirect");
+ this.availableStatisticsFiles.add("advbwdist");
}
public long getLastModified(HttpServletRequest request) {
diff --git a/website/web/WEB-INF/network.jsp b/website/web/WEB-INF/network.jsp
index e0b297a..66b878f 100644
--- a/website/web/WEB-INF/network.jsp
+++ b/website/web/WEB-INF/network.jsp
@@ -296,6 +296,110 @@ the number of written and read dir bytes by all relays.</p>
<a href="dirbytes.svg${dirbytes_url}">SVG</a>.</p>
<p><a href="stats/bandwidth.csv">CSV</a> file containing all data.</p>
<br>
+
+<a name="advbwdist-perc"></a>
+<h3><a href="#advbwdist-perc" class="anchor">Advertised bandwidth
+distribution</a></h3>
+<br>
+<p>The following graph shows the distribution of advertised bandwidth in
+the network. In contrast to the graphs above, the following graph contains
+no sums of advertised bandwidths, but bandwidths of single relays.</p>
+<img src="advbwdist-perc.png${advbwdist_perc_url}"
+ width="576" height="360"
+ alt="Advertised bandwidth distribution graph">
+<form action="network.html#advbwdist-perc">
+ <div class="formrow">
+ <input type="hidden" name="graph" value="advbwdist-perc">
+ <p>
+ <label>Start date (yyyy-mm-dd):</label>
+ <input type="text" name="start" size="10"
+ value="<c:choose><c:when test="${fn:length(advbwdist_perc_start) == 0}">${default_start_date}</c:when><c:otherwise>${advbwdist_perc_start[0]}</c:otherwise></c:choose>">
+ <label>End date (yyyy-mm-dd):</label>
+ <input type="text" name="end" size="10"
+ value="<c:choose><c:when test="${fn:length(advbwdist_perc_end) == 0}">${default_end_date}</c:when><c:otherwise>${advbwdist_perc_end[0]}</c:otherwise></c:choose>">
+ </p><p>
+ <label>Percentiles: </label>
+ <input type="checkbox" name="p" value="100"<c:if test="${fn:length(advbwdist_perc_p) == 0 or fn:contains(fn:join(advbwdist_perc_p, ','), '100')}"> checked</c:if>> 100 (maximum)
+ <input type="checkbox" name="p" value="99"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '99')}"> checked</c:if>> 99
+ <input type="checkbox" name="p" value="98"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '98')}"> checked</c:if>> 98
+ <input type="checkbox" name="p" value="97"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '97')}"> checked</c:if>> 97
+ <input type="checkbox" name="p" value="95"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '95')}"> checked</c:if>> 95
+ <input type="checkbox" name="p" value="91"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '91')}"> checked</c:if>> 91
+ <input type="checkbox" name="p" value="90"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '90')}"> checked</c:if>> 90
+ <input type="checkbox" name="p" value="80"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '80')}"> checked</c:if>> 80
+ <input type="checkbox" name="p" value="75"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '75')}"> checked</c:if>> 75 (3rd quartile)
+ <input type="checkbox" name="p" value="70"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '70')}"> checked</c:if>> 70
+ <input type="checkbox" name="p" value="60"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '60')}"> checked</c:if>> 60
+ <input type="checkbox" name="p" value="50"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '50')}"> checked</c:if>> 50 (median)
+ <input type="checkbox" name="p" value="40"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '40')}"> checked</c:if>> 40
+ <input type="checkbox" name="p" value="30"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '30')}"> checked</c:if>> 30
+ <input type="checkbox" name="p" value="25"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '25')}"> checked</c:if>> 25 (first quartile)
+ <input type="checkbox" name="p" value="20"<c:if test="${fn:length(advbwdist_perc_p) > 0 and fn:contains(fn:join(advbwdist_perc_p, ','), '20')}"> checked</c:if>> 20
+ <input type="checkbox" name="p" value="10"<c:if test="${fn:length(advbwdist_perc_p) > 0 and (fn:startsWith(fn:join(advbwdist_perc_p, ','), '10,') or fn:contains(fn:join(advbwdist_perc_p, ','), ',10,') or fn:endsWith(fn:join(advbwdist_perc_p, ','), ',10') or (advbwdist_perc_p[0] == '10' and fn:length(advbwdist_perc_p) == 1))}"> checked</c:if>> 10
+ <input type="checkbox" name="p" value="9"<c:if test="${fn:length(advbwdist_perc_p) > 0 and (fn:startsWith(fn:join(advbwdist_perc_p, ','), '9,') or fn:contains(fn:join(advbwdist_perc_p, ','), ',9,') or fn:endsWith(fn:join(advbwdist_perc_p, ','), ',9') or (advbwdist_perc_p[0] == '9' and fn:length(advbwdist_perc_p) == 1))}"> checked</c:if>> 9
+ <input type="checkbox" name="p" value="5"<c:if test="${fn:length(advbwdist_perc_p) > 0 and (fn:startsWith(fn:join(advbwdist_perc_p, ','), '5,') or fn:contains(fn:join(advbwdist_perc_p, ','), ',5,') or fn:endsWith(fn:join(advbwdist_perc_p, ','), ',5') or (advbwdist_perc_p[0] == '5' and fn:length(advbwdist_perc_p) == 1))}"> checked</c:if>> 5
+ <input type="checkbox" name="p" value="3"<c:if test="${fn:length(advbwdist_perc_p) > 0 and (fn:startsWith(fn:join(advbwdist_perc_p, ','), '3,') or fn:contains(fn:join(advbwdist_perc_p, ','), ',3,') or fn:endsWith(fn:join(advbwdist_perc_p, ','), ',3') or (advbwdist_perc_p[0] == '3' and fn:length(advbwdist_perc_p) == 1))}"> checked</c:if>> 3
+ <input type="checkbox" name="p" value="2"<c:if test="${fn:length(advbwdist_perc_p) > 0 and (fn:startsWith(fn:join(advbwdist_perc_p, ','), '2,') or fn:contains(fn:join(advbwdist_perc_p, ','), ',2,') or fn:endsWith(fn:join(advbwdist_perc_p, ','), ',2') or (advbwdist_perc_p[0] == '2' and fn:length(advbwdist_perc_p) == 1))}"> checked</c:if>> 2
+ <input type="checkbox" name="p" value="1"<c:if test="${fn:length(advbwdist_perc_p) > 0 and (fn:startsWith(fn:join(advbwdist_perc_p, ','), '1,') or fn:contains(fn:join(advbwdist_perc_p, ','), ',1,') or fn:endsWith(fn:join(advbwdist_perc_p, ','), ',1') or (advbwdist_perc_p[0] == '1' and fn:length(advbwdist_perc_p) == 1))}"> checked</c:if>> 1
+ <input type="checkbox" name="p" value="0"<c:if test="${fn:length(advbwdist_perc_p) > 0 and (fn:startsWith(fn:join(advbwdist_perc_p, ','), '0,') or fn:contains(fn:join(advbwdist_perc_p, ','), ',0,') or fn:endsWith(fn:join(advbwdist_perc_p, ','), ',0') or (advbwdist_perc_p[0] == '0' and fn:length(advbwdist_perc_p) == 1))}"> checked</c:if>> 0 (minimum)
+ </p><p>
+ <input class="submit" type="submit" value="Update graph">
+ </p>
+ </div>
+</form>
+<p>Download graph as
+<a href="advbwdist-perc.pdf${advbwdist_perc_url}">PDF</a> or
+<a href="advbwdist-perc.svg${advbwdist_perc_url}">SVG</a>.</p>
+<p><a href="stats/advbwdist.csv">CSV</a> file containing all data.</p>
+<br>
+
+<a name="advbwdist-relay"></a>
+<h3><a href="#advbwdist-relay" class="anchor">Advertised bandwidth of
+n-th fastest relays</a></h3>
+<br>
+<p>The following graph shows the advertised bandwidth of the n-th fastest
+relays in the network.</p>
+<img src="advbwdist-relay.png${advbwdist_relay_url}"
+ width="576" height="360"
+ alt="Advertised bandwidth of n-th fastest relays graph">
+<form action="network.html#advbwdist-relay">
+ <div class="formrow">
+ <input type="hidden" name="graph" value="advbwdist-relay">
+ <p>
+ <label>Start date (yyyy-mm-dd):</label>
+ <input type="text" name="start" size="10"
+ value="<c:choose><c:when test="${fn:length(advbwdist_relay_start) == 0}">${default_start_date}</c:when><c:otherwise>${advbwdist_relay_start[0]}</c:otherwise></c:choose>">
+ <label>End date (yyyy-mm-dd):</label>
+ <input type="text" name="end" size="10"
+ value="<c:choose><c:when test="${fn:length(advbwdist_relay_end) == 0}">${default_end_date}</c:when><c:otherwise>${advbwdist_relay_end[0]}</c:otherwise></c:choose>">
+ </p><p>
+ <label>n-th fastest relays: </label>
+ <input type="checkbox" name="n" value="1"<c:if test="${fn:length(advbwdist_relay_n) == 0 or fn:contains(fn:join(advbwdist_relay_n, ','), '1,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '1')}"> checked</c:if>> 1
+ <input type="checkbox" name="n" value="2"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '2,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '2'))}"> checked</c:if>> 2
+ <input type="checkbox" name="n" value="3"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '3,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '3'))}"> checked</c:if>> 3
+ <input type="checkbox" name="n" value="5"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '5,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '5'))}"> checked</c:if>> 5
+ <input type="checkbox" name="n" value="10"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '10,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '10'))}"> checked</c:if>> 10
+ <input type="checkbox" name="n" value="20"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '20,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '20'))}"> checked</c:if>> 20
+ <input type="checkbox" name="n" value="30"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '30,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '30'))}"> checked</c:if>> 30
+ <input type="checkbox" name="n" value="50"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '50,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '50'))}"> checked</c:if>> 50
+ <input type="checkbox" name="n" value="100"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '100,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '100'))}"> checked</c:if>> 100
+ <input type="checkbox" name="n" value="200"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '200,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '200'))}"> checked</c:if>> 200
+ <input type="checkbox" name="n" value="300"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '300,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '300'))}"> checked</c:if>> 300
+ <input type="checkbox" name="n" value="500"<c:if test="${fn:length(advbwdist_relay_n) > 0 and (fn:contains(fn:join(advbwdist_relay_n, ','), '500,') or fn:endsWith(fn:join(advbwdist_relay_n, ','), '500'))}"> checked</c:if>> 500
+ <input type="checkbox" name="n" value="1000"<c:if test="${fn:length(advbwdist_relay_n) > 0 and fn:contains(fn:join(advbwdist_relay_n, ','), '1000')}"> checked</c:if>> 1000
+ <input type="checkbox" name="n" value="2000"<c:if test="${fn:length(advbwdist_relay_n) > 0 and fn:contains(fn:join(advbwdist_relay_n, ','), '2000')}"> checked</c:if>> 2000
+ <input type="checkbox" name="n" value="3000"<c:if test="${fn:length(advbwdist_relay_n) > 0 and fn:contains(fn:join(advbwdist_relay_n, ','), '3000')}"> checked</c:if>> 3000
+ <input type="checkbox" name="n" value="5000"<c:if test="${fn:length(advbwdist_relay_n) > 0 and fn:contains(fn:join(advbwdist_relay_n, ','), '5000')}"> checked</c:if>> 5000
+ </p><p>
+ <input class="submit" type="submit" value="Update graph">
+ </p>
+ </div>
+</form>
+<p>Download graph as
+<a href="advbwdist-relay.pdf${advbwdist_relay_url}">PDF</a> or
+<a href="advbwdist-relay.svg${advbwdist_relay_url}">SVG</a>.</p>
+<p><a href="stats/advbwdist.csv">CSV</a> file containing all data.</p>
+<br>
</div>
</div>
<div class="bottom" id="bottom">
diff --git a/website/web/WEB-INF/stats.jsp b/website/web/WEB-INF/stats.jsp
index ccc2540..bdbe4d9 100644
--- a/website/web/WEB-INF/stats.jsp
+++ b/website/web/WEB-INF/stats.jsp
@@ -136,6 +136,35 @@ relays when serving directory data.</li>
<hr>
<br>
+<a name="advbwdist"></a>
+<h3><a href="#advbwdist" class="anchor">Advertised bandwidth distribution
+and n-th fastest relays</a></h3>
+<br>
+<p>Statistics file <a href="stats/advbwdist.csv">advbwdist.csv</a>
+contains statistics on the advertised bandwidth of relays in the network.
+These statistics include advertised bandwidth percentiles and advertised
+bandwidth values of the n-th fastest relays.
+The statistics file contains the following columns:</p>
+
+<ul>
+<li><b>date:</b> UTC date (YYYY-MM-DD) when relays have been listed as
+running.</li>
+<li><b>isexit:</b> Whether relays included in this line have the
+<b>"Exit"</b> relay flag, which would be indicated as <b>"t"</b>.
+If this column contains the empty string, advertised bandwidths from all
+running relays are included, regardless of assigned relay flags.</li>
+<li><b>relay:</b> Position of the relay in an ordered list of all
+advertised bandwidths, starting at 1 for the fastest relay in the network.
+May be the empty string if this line contains advertised bandwidth by
+percentile.</li>
+<li><b>percentile:</b> Advertised bandwidth percentile given in this line.
+May be the empty string if this line contains advertised bandwidth by
+fastest relays.</li>
+<li><b>advbw:</b> Advertised bandwidth in B/s.</li>
+</ul>
+<hr>
+<br>
+
<a name="fast-exits"></a>
<h3><a href="#fast-exits" class="anchor">Relays meeting or almost meeting
fast-exit requirements</a></h3>
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits