[tor-commits] [metrics-lib/master] Parse the new flag-thresholds line in votes.

karsten at torproject.org karsten at torproject.org
Tue Feb 5 14:04:47 UTC 2013


commit c2a0dbf8bf100a19660ad512b88d93f3d7c18a1e
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date:   Tue Feb 5 14:49:28 2013 +0100

    Parse the new flag-thresholds line in votes.
---
 .../descriptor/RelayNetworkStatusVote.java         |   41 ++++++++
 .../descriptor/impl/NetworkStatusEntryImpl.java    |    4 +-
 .../torproject/descriptor/impl/ParseHelper.java    |   26 ++++--
 .../impl/RelayNetworkStatusConsensusImpl.java      |    8 +-
 .../impl/RelayNetworkStatusVoteImpl.java           |  102 +++++++++++++++++++-
 .../impl/RelayNetworkStatusVoteImplTest.java       |   82 ++++++++++++++++
 6 files changed, 246 insertions(+), 17 deletions(-)

diff --git a/src/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
index eda0cef..98abb14 100644
--- a/src/org/torproject/descriptor/RelayNetworkStatusVote.java
+++ b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
@@ -44,6 +44,47 @@ public interface RelayNetworkStatusVote extends Descriptor {
   /* Return known relay flags. */
   public SortedSet<String> getKnownFlags();
 
+  /* Return the minimum uptime in seconds that this authority requires for
+   * assigning the Stable flag, or -1 if the authority doesn't report this
+   * value. */
+  public long getStableUptime();
+
+  /* Return the minimum MTBF (mean time between failure) that this
+   * authority requires for assigning the Stable flag, or -1 if the
+   * authority doesn't report this value. */
+  public long getStableMtbf();
+
+  /* Return the minimum bandwidth that this authority requires for
+   * assigning the Fast flag, or -1 if the authority doesn't report this
+   * value. */
+  public long getFastBandwidth();
+
+  /* Return the minimum WFU (weighted fractional uptime) in percent that
+   * this authority requires for assigning the Guard flag, or -1.0 if the
+   * authority doesn't report this value. */
+  public double getGuardWfu();
+
+  /* Return the minimum time in seconds that this authority needs to know
+   * about a relay before assigning the Guard flag, or -1 if the authority
+   * doesn't report this information. */
+  public long getGuardTk();
+
+  /* Return the minimum bandwidth that this authority requires for
+   * assigning the Guard flag if exits can be guards, or -1 if the
+   * authority doesn't report this value. */
+  public long getGuardBandwidthIncludingExits();
+
+  /* Return the minimum bandwidth that this authority requires for
+   * assigning the Guard flag if exits can not be guards, or -1 if the
+   * authority doesn't report this value. */
+  public long getGuardBandwidthExcludingExits();
+
+  /* Return 1 if the authority has measured enough MTBF info to use the
+   * MTBF requirement instead of the uptime requirement for assigning the
+   * Stable flag, 0 if not, or -1 if the authority doesn't report this
+   * information. */
+  public int getEnoughMtbfInfo();
+
   /* Return consensus parameters. */
   public SortedMap<String, Integer> getConsensusParams();
 
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
index 121f66e..901e17e 100644
--- a/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
+++ b/src/org/torproject/descriptor/impl/NetworkStatusEntryImpl.java
@@ -149,8 +149,8 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
   private void parseWLine(String line, String[] parts)
       throws DescriptorParseException {
     this.parsedAtMostOnceKeyword("w");
-    SortedMap<String, Integer> pairs = ParseHelper.parseKeyValuePairs(
-        line, parts, 1, "=");
+    SortedMap<String, Integer> pairs =
+        ParseHelper.parseKeyValueIntegerPairs(line, parts, 1, "=");
     if (pairs.isEmpty()) {
       throw new DescriptorParseException("Illegal line '" + line + "'.");
     }
diff --git a/src/org/torproject/descriptor/impl/ParseHelper.java b/src/org/torproject/descriptor/impl/ParseHelper.java
index 61d1eb0..1b7eff2 100644
--- a/src/org/torproject/descriptor/impl/ParseHelper.java
+++ b/src/org/torproject/descriptor/impl/ParseHelper.java
@@ -6,6 +6,7 @@ import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.SortedMap;
 import java.util.TimeZone;
 import java.util.TreeMap;
@@ -182,10 +183,10 @@ public class ParseHelper {
     return hexString.toUpperCase();
   }
 
-  public static SortedMap<String, Integer> parseKeyValuePairs(String line,
-      String[] parts, int startIndex, String separatorString)
+  public static SortedMap<String, String> parseKeyValueStringPairs(
+      String line, String[] parts, int startIndex, String separatorString)
       throws DescriptorParseException {
-    SortedMap<String, Integer> result = new TreeMap<String, Integer>();
+    SortedMap<String, String> result = new TreeMap<String, String>();
     for (int i = startIndex; i < parts.length; i++) {
       String pair = parts[i];
       String[] pairParts = pair.split(separatorString);
@@ -193,11 +194,22 @@ public class ParseHelper {
         throw new DescriptorParseException("Illegal key-value pair in "
             + "line '" + line + "'.");
       }
-      String pairName = pairParts[0];
+      result.put(pairParts[0], pairParts[1]);
+    }
+    return result;
+  }
+
+  public static SortedMap<String, Integer> parseKeyValueIntegerPairs(
+      String line, String[] parts, int startIndex, String separatorString)
+      throws DescriptorParseException {
+    SortedMap<String, Integer> result = new TreeMap<String, Integer>();
+    SortedMap<String, String> keyValueStringPairs =
+        ParseHelper.parseKeyValueStringPairs(line, parts, startIndex,
+        separatorString);
+    for (Map.Entry<String, String> e : keyValueStringPairs.entrySet()) {
       try {
-        int pairValue = Integer.parseInt(pairParts[1]);
-        result.put(pairName, pairValue);
-      } catch (NumberFormatException e) {
+        result.put(e.getKey(), Integer.parseInt(e.getValue()));
+      } catch (NumberFormatException ex) {
         throw new DescriptorParseException("Illegal value in line '"
             + line + "'.");
       }
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
index 35cb316..dad5bc3 100644
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -257,14 +257,14 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
 
   private void parseParamsLine(String line, String[] parts)
       throws DescriptorParseException {
-    this.consensusParams = ParseHelper.parseKeyValuePairs(line, parts, 1,
-        "=");
+    this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
+        parts, 1, "=");
   }
 
   private void parseBandwidthWeightsLine(String line, String[] parts)
       throws DescriptorParseException {
-    this.bandwidthWeights = ParseHelper.parseKeyValuePairs(line, parts, 1,
-        "=");
+    this.bandwidthWeights = ParseHelper.parseKeyValueIntegerPairs(line,
+        parts, 1, "=");
   }
 
   private String consensusDigest;
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
index 3695074..c5ba980 100644
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ b/src/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -6,6 +6,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Scanner;
 import java.util.Set;
 import java.util.SortedMap;
@@ -48,14 +49,28 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
         + "dir-key-certification,directory-signature").split(",")));
     this.checkExactlyOnceKeywords(exactlyOnceKeywords);
     Set<String> atMostOnceKeywords = new HashSet<String>(Arrays.asList((
-        "client-versions,server-versions,params,contact,legacy-key,"
-        + "dir-key-crosscert,dir-address,directory-footer").split(",")));
+        "client-versions,server-versions,flag-thresholds,params,contact,"
+        + "legacy-key,dir-key-crosscert,dir-address,directory-footer").
+        split(",")));
     this.checkAtMostOnceKeywords(atMostOnceKeywords);
     this.checkFirstKeyword("network-status-version");
   }
 
   protected void parseHeader(byte[] headerBytes)
       throws DescriptorParseException {
+    /* Initialize flag-thresholds values here for the case that the vote
+     * doesn't contain those values.  Initializing them in the constructor
+     * or when declaring variables wouldn't work, because those parts are
+     * evaluated later and would overwrite everything we parse here. */
+    this.stableUptime = -1L;
+    this.stableMtbf = -1L;
+    this.fastBandwidth = -1L;
+    this.guardWfu = -1.0;
+    this.guardTk = -1L;
+    this.guardBandwidthIncludingExits = -1L;
+    this.guardBandwidthExcludingExits = -1L;
+    this.enoughMtbfInfo = -1;
+
     Scanner s = new Scanner(new String(headerBytes)).useDelimiter("\n");
     boolean skipCrypto = false; /* TODO Parse crypto parts. */
     while (s.hasNext()) {
@@ -84,6 +99,8 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
         this.parseServerVersionsLine(line, parts);
       } else if (keyword.equals("known-flags")) {
         this.parseKnownFlagsLine(line, parts);
+      } else if (keyword.equals("flag-thresholds")) {
+        this.parseFlagThresholdsLine(line, parts);
       } else if (keyword.equals("params")) {
         this.parseParamsLine(line, parts);
       } else if (keyword.equals("dir-source")) {
@@ -247,10 +264,47 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
     }
   }
 
+  private void parseFlagThresholdsLine(String line, String[] parts)
+      throws DescriptorParseException {
+    if (parts.length < 2) {
+      throw new DescriptorParseException("No flag thresholds in line '"
+          + line + "'.");
+    }
+    SortedMap<String, String> flagThresholds =
+        ParseHelper.parseKeyValueStringPairs(line, parts, 1, "=");
+    try {
+      for (Map.Entry<String, String> e : flagThresholds.entrySet()) {
+        if (e.getKey().equals("stable-uptime")) {
+          this.stableUptime = Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("stable-mtbf")) {
+          this.stableMtbf = Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("fast-speed")) {
+          this.fastBandwidth = Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("guard-wfu")) {
+          this.guardWfu = Double.parseDouble(e.getValue().
+              replaceAll("%", ""));
+        } else if (e.getKey().equals("guard-tk")) {
+          this.guardTk = Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("guard-bw-inc-exits")) {
+          this.guardBandwidthIncludingExits =
+              Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("guard-bw-exc-exits")) {
+          this.guardBandwidthExcludingExits =
+              Long.parseLong(e.getValue());
+        } else if (e.getKey().equals("enough-mtbf")) {
+          this.enoughMtbfInfo = Integer.parseInt(e.getValue());
+        }
+      }
+    } catch (NumberFormatException ex) {
+      throw new DescriptorParseException("Illegal value in line '"
+          + line + "'.");
+    }
+  }
+
   private void parseParamsLine(String line, String[] parts)
       throws DescriptorParseException {
-    this.consensusParams = ParseHelper.parseKeyValuePairs(line, parts, 1,
-        "=");
+    this.consensusParams = ParseHelper.parseKeyValueIntegerPairs(line,
+        parts, 1, "=");
   }
 
   private void parseDirSourceLine(String line, String[] parts)
@@ -465,6 +519,46 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
     return new TreeSet<String>(this.knownFlags);
   }
 
+  private long stableUptime;
+  public long getStableUptime() {
+    return this.stableUptime;
+  }
+
+  private long stableMtbf;
+  public long getStableMtbf() {
+    return this.stableMtbf;
+  }
+
+  private long fastBandwidth;
+  public long getFastBandwidth() {
+    return this.fastBandwidth;
+  }
+
+  private double guardWfu;
+  public double getGuardWfu() {
+    return this.guardWfu;
+  }
+
+  private long guardTk;
+  public long getGuardTk() {
+    return this.guardTk;
+  }
+
+  private long guardBandwidthIncludingExits;
+  public long getGuardBandwidthIncludingExits() {
+    return this.guardBandwidthIncludingExits;
+  }
+
+  private long guardBandwidthExcludingExits;
+  public long getGuardBandwidthExcludingExits() {
+    return this.guardBandwidthExcludingExits;
+  }
+
+  private int enoughMtbfInfo;
+  public int getEnoughMtbfInfo() {
+    return this.enoughMtbfInfo;
+  }
+
   private SortedMap<String, Integer> consensusParams;
   public SortedMap<String, Integer> getConsensusParams() {
     return this.consensusParams == null ? null:
diff --git a/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java b/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
index 7fbdb7d..b3fbcfd 100644
--- a/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
+++ b/test/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java
@@ -115,6 +115,17 @@ public class RelayNetworkStatusVoteImplTest {
       vb.knownFlagsLine = line;
       return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
     }
+    private String flagThresholdsLine = "flag-thresholds "
+        + "stable-uptime=693369 stable-mtbf=153249 fast-speed=40960 "
+        + "guard-wfu=94.669% guard-tk=691200 guard-bw-inc-exits=174080 "
+        + "guard-bw-exc-exits=184320 enough-mtbf=1";
+    private static RelayNetworkStatusVote
+        createWithFlagThresholdsLine(String line)
+        throws DescriptorParseException {
+      VoteBuilder vb = new VoteBuilder();
+      vb.flagThresholdsLine = line;
+      return new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+    }
     private String paramsLine = "params "
         + "CircuitPriorityHalflifeMsec=30000 bwauthbestratio=1 "
         + "bwauthcircs=1 bwauthdescbw=0 bwauthkp=10000 bwauthpid=1 "
@@ -388,6 +399,9 @@ public class RelayNetworkStatusVoteImplTest {
       if (this.knownFlagsLine != null) {
         sb.append(this.knownFlagsLine + "\n");
       }
+      if (this.flagThresholdsLine != null) {
+        sb.append(this.flagThresholdsLine + "\n");
+      }
       if (this.paramsLine != null) {
         sb.append(this.paramsLine + "\n");
       }
@@ -749,6 +763,74 @@ public class RelayNetworkStatusVoteImplTest {
     VoteBuilder.createWithKnownFlagsLine("known-flags ");
   }
 
+  @Test()
+  public void testFlagThresholdsLine() throws DescriptorParseException {
+    VoteBuilder vb = new VoteBuilder();
+    RelayNetworkStatusVote vote =
+        new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+    assertEquals(693369L, vote.getStableUptime());
+    assertEquals(153249L, vote.getStableMtbf());
+    assertEquals(40960L, vote.getFastBandwidth());
+    assertEquals(94.669, vote.getGuardWfu(), 0.001);
+    assertEquals(691200L, vote.getGuardTk());
+    assertEquals(174080L, vote.getGuardBandwidthIncludingExits());
+    assertEquals(184320L, vote.getGuardBandwidthExcludingExits());
+    assertEquals(1, vote.getEnoughMtbfInfo());
+  }
+
+  @Test()
+  public void testFlagThresholdsNoLine() throws DescriptorParseException {
+    RelayNetworkStatusVote vote =
+        VoteBuilder.createWithFlagThresholdsLine(null);
+    assertEquals(-1L, vote.getStableUptime());
+    assertEquals(-1L, vote.getStableMtbf());
+    assertEquals(-1L, vote.getFastBandwidth());
+    assertEquals(-1.0, vote.getGuardWfu(), 0.001);
+    assertEquals(-1L, vote.getGuardTk());
+    assertEquals(-1L, vote.getGuardBandwidthIncludingExits());
+    assertEquals(-1L, vote.getGuardBandwidthExcludingExits());
+    assertEquals(-1, vote.getEnoughMtbfInfo());
+  }
+
+  @Test()
+  public void testFlagThresholdsAllZeroes()
+      throws DescriptorParseException {
+    RelayNetworkStatusVote vote =
+        VoteBuilder.createWithFlagThresholdsLine("flag-thresholds "
+            + "stable-uptime=0 stable-mtbf=0 fast-speed=0 guard-wfu=0.0% "
+            + "guard-tk=0 guard-bw-inc-exits=0 guard-bw-exc-exits=0 "
+            + "enough-mtbf=0");
+    assertEquals(0L, vote.getStableUptime());
+    assertEquals(0L, vote.getStableMtbf());
+    assertEquals(0L, vote.getFastBandwidth());
+    assertEquals(0.0, vote.getGuardWfu(), 0.001);
+    assertEquals(0L, vote.getGuardTk());
+    assertEquals(0L, vote.getGuardBandwidthIncludingExits());
+    assertEquals(0L, vote.getGuardBandwidthExcludingExits());
+    assertEquals(0, vote.getEnoughMtbfInfo());
+  }
+
+  @Test(expected = DescriptorParseException.class)
+  public void testFlagThresholdsNoSpace()
+      throws DescriptorParseException {
+    VoteBuilder.createWithFlagThresholdsLine("flag-thresholds");
+  }
+
+  @Test(expected = DescriptorParseException.class)
+  public void testFlagThresholdsOneSpace()
+      throws DescriptorParseException {
+    VoteBuilder.createWithFlagThresholdsLine("flag-thresholds ");
+  }
+
+  @Test(expected = DescriptorParseException.class)
+  public void testFlagThresholdDuplicate()
+      throws DescriptorParseException {
+    VoteBuilder vb = new VoteBuilder();
+    vb.flagThresholdsLine = vb.flagThresholdsLine + "\n"
+        + vb.flagThresholdsLine;
+    new RelayNetworkStatusVoteImpl(vb.buildVote(), true);
+  }
+
   @Test(expected = DescriptorParseException.class)
   public void testNicknameMissing() throws DescriptorParseException {
     VoteBuilder.createWithDirSourceLine("dir-source  "



More information about the tor-commits mailing list