[tor-commits] [metrics-lib/master] Parse new NAT-based Snowflake lines.

2020-12-18 Thread karsten
commit 8976cdd9be1bb70d3457bf2551971e12ee253ce1
Author: Karsten Loesing 
Date:   Fri Dec 18 12:02:55 2020 +0100

Parse new NAT-based Snowflake lines.

Implements #40002.
---
 CHANGELOG.md   |  5 +-
 .../org/torproject/descriptor/SnowflakeStats.java  | 54 
 .../java/org/torproject/descriptor/impl/Key.java   |  5 ++
 .../descriptor/impl/SnowflakeStatsImpl.java| 75 ++
 .../descriptor/impl/SnowflakeStatsImplTest.java| 50 +++
 5 files changed, 188 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac865a9..68ad2a2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,7 @@
-# Changes in version 2.??.? - 2020-??-??
+# Changes in version 2.16.0 - 2020-??-??
+
+ * Medium changes
+   - Parse new NAT-based Snowflake lines.
 
 
 # Changes in version 2.15.0 - 2020-12-11
diff --git a/src/main/java/org/torproject/descriptor/SnowflakeStats.java 
b/src/main/java/org/torproject/descriptor/SnowflakeStats.java
index 2fe78df..967d061 100644
--- a/src/main/java/org/torproject/descriptor/SnowflakeStats.java
+++ b/src/main/java/org/torproject/descriptor/SnowflakeStats.java
@@ -103,6 +103,30 @@ public interface SnowflakeStats extends Descriptor {
*/
   Optional clientDeniedCount();
 
+  /**
+   * Return a count of the number of times a client with a restricted or 
unknown
+   * NAT type has requested a proxy from the broker but no proxies were
+   * available, rounded up to the nearest multiple of 8.
+   *
+   * @return Count of the number of times a client with a restricted or unknown
+   * NAT type has requested a proxy from the broker but no proxies were
+   * available, rounded up to the nearest multiple of 8.
+   * @since 2.16.0
+   */
+  Optional clientRestrictedDeniedCount();
+
+  /**
+   * Return a count of the number of times a client with an unrestricted NAT
+   * type has requested a proxy from the broker but no proxies were available,
+   * rounded up to the nearest multiple of 8.
+   *
+   * @return Count of the number of times a client with an unrestricted NAT 
type
+   * has requested a proxy from the broker but no proxies were available,
+   * rounded up to the nearest multiple of 8.
+   * @since 2.16.0
+   */
+  Optional clientUnrestrictedDeniedCount();
+
   /**
* Return a count of the number of times a client successfully received a
* proxy from the broker, rounded up to the nearest multiple of 8.
@@ -112,5 +136,35 @@ public interface SnowflakeStats extends Descriptor {
* @since 2.7.0
*/
   Optional clientSnowflakeMatchCount();
+
+  /**
+   * Return a count of the total number of unique IP addresses of snowflake
+   * proxies that have a restricted NAT type.
+   *
+   * @return Count of the total number of unique IP addresses of snowflake
+   * proxies that have a restricted NAT type.
+   * @since 2.16.0
+   */
+  Optional snowflakeIpsNatRestricted();
+
+  /**
+   * Return a count of the total number of unique IP addresses of snowflake
+   * proxies that have an unrestricted NAT type.
+   *
+   * @return Count of the total number of unique IP addresses of snowflake
+   * proxies that have an unrestricted NAT type.
+   * @since 2.16.0
+   */
+  Optional snowflakeIpsNatUnrestricted();
+
+  /**
+   * Return a count of the total number of unique IP addresses of snowflake
+   * proxies that have an unknown NAT type.
+   *
+   * @return Count of the total number of unique IP addresses of snowflake
+   * proxies that have an unknown NAT type.
+   * @since 2.16.0
+   */
+  Optional snowflakeIpsNatUnknown();
 }
 
diff --git a/src/main/java/org/torproject/descriptor/impl/Key.java 
b/src/main/java/org/torproject/descriptor/impl/Key.java
index b02d96e..410cef6 100644
--- a/src/main/java/org/torproject/descriptor/impl/Key.java
+++ b/src/main/java/org/torproject/descriptor/impl/Key.java
@@ -36,7 +36,9 @@ public enum Key {
   CELL_STATS_END("cell-stats-end"),
   CELL_TIME_IN_QUEUE("cell-time-in-queue"),
   CLIENT_DENIED_COUNT("client-denied-count"),
+  CLIENT_RESTRICTED_DENIED_COUNT("client-restricted-denied-count"),
   CLIENT_SNOWFLAKE_MATCH_COUNT("client-snowflake-match-count"),
+  CLIENT_UNRESTRICTED_DENIED_COUNT("client-unrestricted-denied-count"),
   CLIENT_VERSIONS("client-versions"),
   CONN_BI_DIRECT("conn-bi-direct"),
   CONSENSUS_METHOD("consensus-method"),
@@ -149,6 +151,9 @@ public enum Key {
   SNOWFLAKE_IDLE_COUNT("snowflake-idle-count"),
   SNOWFLAKE_IPS("snowflake-ips"),
   SNOWFLAKE_IPS_BADGE("snowflake-ips-badge"),
+  SNOWFLAKE_IPS_NAT_RESTRICTED("snowflake-ips-nat-restricted"),
+  SNOWFLAKE_IPS_NAT_UNKNOWN("snowflake-ips-nat-unknown"),
+  SNOWFLAKE_IPS_NAT_UNRESTRICTED("snowflake-ips-nat-unrestricted"),
   SNOWFLAKE_IPS_STANDALONE("snowflake-ips-standalone&qu

[tor-commits] [metrics-web/master] Update to metrics-lib 2.15.0.

2020-12-17 Thread karsten
commit a07829a30bb85864f46f8668882689d16382ad6a
Author: Karsten Loesing 
Date:   Thu Dec 17 15:27:02 2020 +0100

Update to metrics-lib 2.15.0.
---
 build.xml   | 2 +-
 src/submods/metrics-lib | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/build.xml b/build.xml
index 0d82a81..8cfc81a 100644
--- a/build.xml
+++ b/build.xml
@@ -10,7 +10,7 @@
   
   
   
-  
+  
   
   
   https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [metrics-web/master] Update instructions when javascript is disabled.

2020-12-17 Thread karsten
commit 07f70b1ce967585c2307ea1170f85a9274b04569
Author: Karsten Loesing 
Date:   Thu Dec 17 15:29:11 2020 +0100

Update instructions when javascript is disabled.

Fixes #31714.
---
 src/main/resources/web/jsps/rs.jsp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/resources/web/jsps/rs.jsp 
b/src/main/resources/web/jsps/rs.jsp
index ec635f7..281f932 100644
--- a/src/main/resources/web/jsps/rs.jsp
+++ b/src/main/resources/web/jsps/rs.jsp
@@ -29,7 +29,7 @@
 
   
   JavaScript required
-  Please enable JavaScript to use this service. If you are using Tor 
Browser on High Security mode, it is possible to enable JavaScript to run only 
on this page. Click the NoScript  icon on your 
address bar and select "Temporarily allow all on this page". Relay Search only 
uses JavaScript resources that are hosted by the Tor Metrics team.
+  Please enable JavaScript to use this service. If you are using Tor 
Browser on Safest mode, you'll have to switch to Safer or Standard mode. Relay 
Search only uses JavaScript resources that are hosted by the Tor Metrics 
team.
 
   
  



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [metrics-web/master] Update news.json.

2020-12-17 Thread karsten
commit 5df9d4c9115b4d289ee1cb428f0e433549b88718
Author: Karsten Loesing 
Date:   Thu Dec 17 15:31:35 2020 +0100

Update news.json.
---
 src/main/resources/web/json/news.json | 119 +-
 1 file changed, 118 insertions(+), 1 deletion(-)

diff --git a/src/main/resources/web/json/news.json 
b/src/main/resources/web/json/news.json
index 901b00c..b788fe5 100644
--- a/src/main/resources/web/json/news.json
+++ b/src/main/resources/web/json/news.json
@@ -1,4 +1,56 @@
 [ {
+  "start" : "2020-12-05",
+  "end" : "2020-12-09",
+  "protocols" : [ "meek", "moat" ],
+  "short_description" : "An update to an Azure CDN edge server TLS certificate 
causes an outage of meek and Moat",
+  "description" : "An update to an Azure CDN edge server TLS certificate 
causes an outage of meek and Moat. It is fixed by the release of Tor Browser 
10.0.6, which updates the built-in public key pinning of obfs4proxy.",
+  "links" : [ {
+"label" : "ticket",
+"target" : 
"https://bugs.torproject.org/tpo/anti-censorship/pluggable-transports/meek/40001;
+  }, {
+"label" : "blog post",
+"target" : 
"https://blog.torproject.org/new-release-tor-browser-1006#comments;
+  }, {
+"label" : "meek graph",
+"target" : 
"https://metrics.torproject.org/userstats-bridge-transport.html?start=2020-11-01=2020-12-31=meek;
+  }, {
+"label" : "BridgeDB graph",
+"target" : 
"https://metrics.torproject.org/bridgedb-distributor.html?start=2020-11-01=2020-12-31;
+  }, {
+"label" : "certificate transparency",
+"target" : 
"https://crt.sh/?q=cd29bc427d93bc4453d129a294cfd5e082eacbf3fe9a19b7718a50422b6e6cc5;
+  } ]
+}, {
+  "start" : "2020-11-08",
+  "end" : "2020-11-09",
+  "protocols" : [ "snowflake" ],
+  "short_description" : "Outage of the snowflake bridge",
+  "description" : "Outage of the snowflake bridge",
+  "links" : [ {
+"label" : "ticket",
+"target" : 
"https://bugs.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/40020;
+  } ]
+}, {
+  "start" : "2020-10-27",
+  "ongoing" : true,
+  "places" : [ "tz" ],
+  "protocols" : [ "", "obfs4" ],
+  "short_description" : "Blocking of Tor and default obfs4 bridges during 
elections in Tanzania.",
+  "description" : "Blocking of Tor and default obfs4 bridges during elections 
in Tanzania.",
+  "links" : [ {
+"label" : "OONI report",
+"target" : 
"https://ooni.org/post/2020-tanzania-blocks-social-media-tor-election-day/#blocking-of-tor;
+  } ]
+}, {
+  "start" : "2020-10-05",
+  "protocols" : [ "snowflake" ],
+  "short_description" : "Deployed version 0.4.2 of the Snowflake WebExtension, 
with better counting of clients.",
+  "description" : "Deployed version 0.4.2 of the Snowflake WebExtension, with 
better counting of clients.",
+  "links" : [ {
+"label" : "comment",
+"target" : 
"https://bugs.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/33157#note_2710701;
+  } ]
+}, {
   "start" : "2020-08-31",
   "protocols" : [ "obfs4" ],
   "short_description" : "Shutdown of default obfs4 bridge frosty.",
@@ -16,6 +68,19 @@
 "label" : "comment",
 "target" : 
"https://bugs.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake-webext/13#note_2705805;
   } ]
+}, {
+  "start" : "2020-08-09",
+  "end" : "2020-08-12",
+  "places" : [ "by" ],
+  "short_description" : "Internet shutdowns in Belarus, during protests 
following a presidential election.",
+  "description" : "Internet shutdowns in Belarus, during protests following a 
presidential election.",
+  "links" : [ {
+"label" : "report",
+"target" : 
"https://ooni.org/post/2020-belarus-internet-outages-website-censorship/#internet-outages-amid-2020-belarusian-presidential-election;
+  }, {
+"label" : "thread",
+"target" : "https://github.com/net4people/bbs/issues/46;
+  } ]
 }, {
   "start" : "2020-07-06",
   "protocols" : [ "snowflake" ],
@@ -347,6 +412,9 @@
   "links" : [ {
  

[tor-commits] [collector/master] Include OnionPerf analysis files when syncing.

2020-12-14 Thread karsten
commit 06c0d78e4a73042c0e7fb6052f079237b8d7ff01
Author: Karsten Loesing 
Date:   Mon Dec 14 23:23:54 2020 +0100

Include OnionPerf analysis files when syncing.
---
 CHANGELOG.md   |  4 +-
 .../collector/onionperf/OnionPerfDownloader.java   |  1 +
 .../collector/persist/OnionPerfPersistence.java| 60 +++---
 src/main/resources/collector.properties|  2 +-
 4 files changed, 57 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f56f74d..7956e55 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,8 +4,8 @@
   - Clean up descriptors written to the `out/` directory by deleting
 files that are older than seven weeks.
   - Correctly index files that are moved away and back.
-  - Include microdescriptors and certs when syncing from another
-CollecTor instance.
+  - Include microdescriptors, certs, and OnionPerf analysis files when
+syncing from another CollecTor instance.
   - Update to metrics-lib 2.15.0.
 
 
diff --git 
a/src/main/java/org/torproject/metrics/collector/onionperf/OnionPerfDownloader.java
 
b/src/main/java/org/torproject/metrics/collector/onionperf/OnionPerfDownloader.java
index f90bdfe..352d24a 100644
--- 
a/src/main/java/org/torproject/metrics/collector/onionperf/OnionPerfDownloader.java
+++ 
b/src/main/java/org/torproject/metrics/collector/onionperf/OnionPerfDownloader.java
@@ -57,6 +57,7 @@ public class OnionPerfDownloader extends CollecTorMain {
   public OnionPerfDownloader(Configuration config) {
 super(config);
 this.mapPathDescriptors.put("recent/torperf", TorperfResult.class);
+this.mapPathDescriptors.put("recent/onionperf", TorperfResult.class);
   }
 
   /** File containing the download history, which is necessary, because
diff --git 
a/src/main/java/org/torproject/metrics/collector/persist/OnionPerfPersistence.java
 
b/src/main/java/org/torproject/metrics/collector/persist/OnionPerfPersistence.java
index 7ed16a2..8975d80 100644
--- 
a/src/main/java/org/torproject/metrics/collector/persist/OnionPerfPersistence.java
+++ 
b/src/main/java/org/torproject/metrics/collector/persist/OnionPerfPersistence.java
@@ -6,12 +6,21 @@ package org.torproject.metrics.collector.persist;
 import org.torproject.descriptor.TorperfResult;
 import org.torproject.metrics.collector.conf.Annotation;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
 
 public class OnionPerfPersistence
 extends DescriptorPersistence {
 
+  private static final Logger logger
+  = LoggerFactory.getLogger(OnionPerfPersistence.class);
+
   private static final String ONIONPERF = "torperf";
 
   public OnionPerfPersistence(TorperfResult desc) {
@@ -32,18 +41,55 @@ public class OnionPerfPersistence
 name).toString();
   }
 
-  /** OnionPerf default storage appends. */
+  /** If the original descriptor file was a .tpf file, append the parsed 
Torperf
+   * result to the destination .tpf file, but if it was a .json.xz file, just
+   * copy over the entire file, unless it already exists. */
   @Override
-  public boolean storeOut(String outRoot) {
-return super.storeOut(outRoot, StandardOpenOption.APPEND);
+  public boolean storeOut(String outRoot, StandardOpenOption option) {
+if (desc.getDescriptorFile().getName().endsWith(".tpf")) {
+  return super.storeOut(outRoot, StandardOpenOption.APPEND);
+} else {
+  String fileName = desc.getDescriptorFile().getName();
+  String[] dateParts = fileName.split("\\.")[0].split("-");
+  return this.copyIfNotExists(
+  Paths.get(outRoot,
+  "onionperf",
+  dateParts[0], // year
+  dateParts[1], // month
+  dateParts[2], // day
+  fileName));
+}
   }
 
-  /** OnionPerf default storage appends. */
+  /** If the original descriptor file was a .tpf file, append the parsed 
Torperf
+   * result to the destination .tpf file, but if it was a .json.xz file, just
+   * copy over the entire file, unless it already exists. */
   @Override
-  public boolean storeAll(String recentRoot, String outRoot) {
-return super.storeAll(recentRoot, outRoot, StandardOpenOption.APPEND,
-StandardOpenOption.APPEND);
+  public boolean storeRecent(String recentRoot, StandardOpenOption option) {
+if (desc.getDescriptorFile().getName().endsWith(".tpf")) {
+  return super.storeRecent(recentRoot, StandardOpenOption.APPEND);
+} else {
+  String fileName = desc.getDescriptorFile().getName();
+  return this.copyIfNotExists(
+  Paths.get(recentRoot,
+  "onionperf",
+  fileName));
+}
   }
 
+  private boolean copyIfNotExists(Path destinationFile) {
+if (Files.exists(destinationFile)) {

[tor-commits] [collector/master] Include certs when syncing from another instance.

2020-12-14 Thread karsten
commit 9834cec8c0e88c1e810434f543f5d8c4d5cb2de8
Author: Karsten Loesing 
Date:   Mon Dec 14 09:35:35 2020 +0100

Include certs when syncing from another instance.
---
 CHANGELOG.md   |  4 ++--
 .../DirectoryKeyCertificatePersistence.java| 27 ++
 .../collector/relaydescs/ArchiveWriter.java|  7 +-
 .../metrics/collector/sync/SyncPersistence.java|  6 +
 4 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22d3517..f56f74d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,8 +4,8 @@
   - Clean up descriptors written to the `out/` directory by deleting
 files that are older than seven weeks.
   - Correctly index files that are moved away and back.
-  - Include microdescriptors when syncing from another CollecTor
-instance.
+  - Include microdescriptors and certs when syncing from another
+CollecTor instance.
   - Update to metrics-lib 2.15.0.
 
 
diff --git 
a/src/main/java/org/torproject/metrics/collector/persist/DirectoryKeyCertificatePersistence.java
 
b/src/main/java/org/torproject/metrics/collector/persist/DirectoryKeyCertificatePersistence.java
new file mode 100644
index 000..39f88f3
--- /dev/null
+++ 
b/src/main/java/org/torproject/metrics/collector/persist/DirectoryKeyCertificatePersistence.java
@@ -0,0 +1,27 @@
+/* Copyright 2020 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.metrics.collector.persist;
+
+import org.torproject.descriptor.DirectoryKeyCertificate;
+import org.torproject.metrics.collector.conf.Annotation;
+
+import java.nio.file.Paths;
+
+public class DirectoryKeyCertificatePersistence
+extends DescriptorPersistence {
+
+  public DirectoryKeyCertificatePersistence(
+  DirectoryKeyCertificate descriptor) {
+super(descriptor, Annotation.Cert.bytes());
+this.calculatePaths();
+  }
+
+  private void calculatePaths() {
+String fileName = this.desc.getFingerprint().toUpperCase() + "-"
++ PersistenceUtils.dateTime(this.desc.getDirKeyPublishedMillis());
+this.recentPath = Paths.get(RELAYDESCS, "certs", fileName).toString();
+this.storagePath = this.recentPath;
+  }
+}
+
diff --git 
a/src/main/java/org/torproject/metrics/collector/relaydescs/ArchiveWriter.java 
b/src/main/java/org/torproject/metrics/collector/relaydescs/ArchiveWriter.java
index 5c58f23..616d7dd 100644
--- 
a/src/main/java/org/torproject/metrics/collector/relaydescs/ArchiveWriter.java
+++ 
b/src/main/java/org/torproject/metrics/collector/relaydescs/ArchiveWriter.java
@@ -7,6 +7,7 @@ import org.torproject.descriptor.BandwidthFile;
 import org.torproject.descriptor.Descriptor;
 import org.torproject.descriptor.DescriptorParser;
 import org.torproject.descriptor.DescriptorSourceFactory;
+import org.torproject.descriptor.DirectoryKeyCertificate;
 import org.torproject.descriptor.Microdescriptor;
 import org.torproject.descriptor.RelayExtraInfoDescriptor;
 import org.torproject.descriptor.RelayNetworkStatusConsensus;
@@ -105,6 +106,8 @@ public class ArchiveWriter extends CollecTorMain {
 super(config);
 this.mapPathDescriptors.put("recent/relay-descriptors/votes",
 RelayNetworkStatusVote.class);
+this.mapPathDescriptors.put("recent/relay-descriptors/certs",
+DirectoryKeyCertificate.class);
 this.mapPathDescriptors.put("recent/relay-descriptors/consensuses",
 RelayNetworkStatusConsensus.class);
 this.mapPathDescriptors.put(
@@ -738,7 +741,9 @@ public class ArchiveWriter extends CollecTorMain {
 "-MM-dd-HH-mm-ss");
 File tarballFile = Paths.get(this.outputDirectory, "certs",
 fingerprint + "-" + printFormat.format(new Date(published))).toFile();
-File[] outputFiles = new File[] { tarballFile };
+File rsyncFile = Paths.get(recentPathName, RELAY_DESCRIPTORS, "certs",
+tarballFile.getName()).toFile();
+File[] outputFiles = new File[] { tarballFile, rsyncFile };
 if (this.store(Annotation.Cert.bytes(), data, outputFiles, null)) {
   this.storedCertsCounter++;
 }
diff --git 
a/src/main/java/org/torproject/metrics/collector/sync/SyncPersistence.java 
b/src/main/java/org/torproject/metrics/collector/sync/SyncPersistence.java
index e8f780e..ba06bd1 100644
--- a/src/main/java/org/torproject/metrics/collector/sync/SyncPersistence.java
+++ b/src/main/java/org/torproject/metrics/collector/sync/SyncPersistence.java
@@ -10,6 +10,7 @@ import org.torproject.descriptor.BridgePoolAssignment;
 import org.torproject.descriptor.BridgeServerDescriptor;
 import org.torproject.descriptor.BridgedbMetrics;
 import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DirectoryKeyCertificate;
 import org.torproject.descriptor.ExitList;
 import org.torproject.descriptor.Microdescri

[tor-commits] [collector/master] Make sure that the DirectoryStream gets closed.

2020-12-14 Thread karsten
commit 83850daa9de893cf2d7f846e67191a469cc64900
Author: Karsten Loesing 
Date:   Sat Dec 5 22:22:22 2020 +0100

Make sure that the DirectoryStream gets closed.

As the docs say, "If timely disposal of file system resources is
required, the try-with-resources construct should be used to ensure
that the stream's close method is invoked after the stream operations
are completed."

Turns out that without closing the stream, the JVM runs out of memory
pretty quickly. Doing this is not optional but mandatory.
---
 .../torproject/metrics/collector/persist/PersistenceUtils.java | 10 +++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/persist/PersistenceUtils.java 
b/src/main/java/org/torproject/metrics/collector/persist/PersistenceUtils.java
index 2b7621d..1dc36d6 100644
--- 
a/src/main/java/org/torproject/metrics/collector/persist/PersistenceUtils.java
+++ 
b/src/main/java/org/torproject/metrics/collector/persist/PersistenceUtils.java
@@ -20,6 +20,7 @@ import java.nio.file.attribute.BasicFileAttributes;
 import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.util.Date;
+import java.util.stream.Stream;
 
 public class PersistenceUtils {
 
@@ -132,9 +133,12 @@ public class PersistenceUtils {
   @Override
   public FileVisitResult postVisitDirectory(Path dir, IOException exc)
   throws IOException {
-if (!pathToClean.equals(dir)
-&& !Files.list(dir).findFirst().isPresent()) {
-  Files.delete(dir);
+if (!pathToClean.equals(dir)) {
+  try (Stream files = Files.list(dir)) {
+if (!files.findFirst().isPresent()) {
+  Files.delete(dir);
+}
+  }
 }
 return FileVisitResult.CONTINUE;
   }



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [collector/master] Fix a few paths for cleaning up.

2020-12-14 Thread karsten
commit 4879cdc219c797e3e0a54226978462fe67d7cf3b
Author: Karsten Loesing 
Date:   Fri Dec 11 16:55:37 2020 +0100

Fix a few paths for cleaning up.
---
 .../metrics/collector/bridgedb/BridgedbMetricsProcessor.java| 6 --
 .../metrics/collector/bridgedescs/SanitizedBridgesWriter.java   | 6 --
 .../metrics/collector/snowflake/SnowflakeStatsDownloader.java   | 6 --
 3 files changed, 12 insertions(+), 6 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedb/BridgedbMetricsProcessor.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedb/BridgedbMetricsProcessor.java
index d05aa9c..94f697e 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedb/BridgedbMetricsProcessor.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedb/BridgedbMetricsProcessor.java
@@ -178,9 +178,11 @@ public class BridgedbMetricsProcessor extends 
CollecTorMain {
* in the last three days (seven weeks).
*/
   private void cleanUpDirectories() {
-PersistenceUtils.cleanDirectory(Paths.get(this.recentPathName),
+PersistenceUtils.cleanDirectory(
+Paths.get(this.recentPathName).resolve("bridgedb-metrics"),
 Instant.now().minus(3, ChronoUnit.DAYS).toEpochMilli());
-PersistenceUtils.cleanDirectory(Paths.get(this.outputPathName),
+PersistenceUtils.cleanDirectory(
+Paths.get(this.outputPathName).resolve("bridgedb-metrics"),
 Instant.now().minus(49, ChronoUnit.DAYS).toEpochMilli());
   }
 }
diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
index 7619453..ffafdea 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
@@ -449,9 +449,11 @@ public class SanitizedBridgesWriter extends CollecTorMain {
* in the last three days (seven weeks), and remove the .tmp extension from
* newly written files. */
   private void cleanUpDirectories() {
-PersistenceUtils.cleanDirectory(this.recentDirectory,
+PersistenceUtils.cleanDirectory(
+this.recentDirectory.resolve("bridge-descriptors"),
 Instant.now().minus(3, ChronoUnit.DAYS).toEpochMilli());
-PersistenceUtils.cleanDirectory(this.outputDirectory,
+PersistenceUtils.cleanDirectory(
+this.outputDirectory.resolve("bridge-descriptors"),
 Instant.now().minus(49, ChronoUnit.DAYS).toEpochMilli());
   }
 }
diff --git 
a/src/main/java/org/torproject/metrics/collector/snowflake/SnowflakeStatsDownloader.java
 
b/src/main/java/org/torproject/metrics/collector/snowflake/SnowflakeStatsDownloader.java
index 93388d5..c9b6b03 100644
--- 
a/src/main/java/org/torproject/metrics/collector/snowflake/SnowflakeStatsDownloader.java
+++ 
b/src/main/java/org/torproject/metrics/collector/snowflake/SnowflakeStatsDownloader.java
@@ -156,9 +156,11 @@ public class SnowflakeStatsDownloader extends 
CollecTorMain {
   /** Delete all files from the rsync (out) directory that have not been
* modified in the last three days (seven weeks). */
   private void cleanUpDirectories() {
-PersistenceUtils.cleanDirectory(Paths.get(this.recentPathName),
+PersistenceUtils.cleanDirectory(
+Paths.get(this.recentPathName, "snowflakes"),
 Instant.now().minus(3, ChronoUnit.DAYS).toEpochMilli());
-PersistenceUtils.cleanDirectory(Paths.get(this.outputPathName),
+PersistenceUtils.cleanDirectory(
+Paths.get(this.outputPathName, "snowflakes"),
 Instant.now().minus(49, ChronoUnit.DAYS).toEpochMilli());
   }
 }



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [collector/master] Only clean up a single time during sync.

2020-12-14 Thread karsten
commit 5e5a7d1dfa30ffaf0a27ea4e246de5bef30d1cbc
Author: Karsten Loesing 
Date:   Fri Dec 11 16:55:58 2020 +0100

Only clean up a single time during sync.
---
 src/main/java/org/torproject/metrics/collector/sync/SyncManager.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/sync/SyncManager.java 
b/src/main/java/org/torproject/metrics/collector/sync/SyncManager.java
index 1fa1347..8e9c7d3 100644
--- a/src/main/java/org/torproject/metrics/collector/sync/SyncManager.java
+++ b/src/main/java/org/torproject/metrics/collector/sync/SyncManager.java
@@ -108,11 +108,11 @@ public class SyncManager {
 
   persist.storeDesc(desc, collectionDate.getTime());
 }
-persist.cleanDirectory();
 descriptorReader.saveHistoryFile(historyFile);
   }
   logger.info("Done merging {} from {}.", marker, source.getHost());
 }
+persist.cleanDirectory();
   }
 
 }

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [collector/master] Include microdescriptors when syncing.

2020-12-14 Thread karsten
commit a1b8ebb9545b148524c6e3db2a1601f228784905
Author: Karsten Loesing 
Date:   Wed Dec 9 22:01:21 2020 +0100

Include microdescriptors when syncing.
---
 CHANGELOG.md   |  3 ++
 build.xml  |  2 +-
 .../persist/MicrodescriptorPersistence.java| 36 ++
 .../collector/relaydescs/ArchiveWriter.java| 11 +--
 .../metrics/collector/sync/SyncPersistence.java| 10 ++
 5 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2928937..22d3517 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@
   - Clean up descriptors written to the `out/` directory by deleting
 files that are older than seven weeks.
   - Correctly index files that are moved away and back.
+  - Include microdescriptors when syncing from another CollecTor
+instance.
+  - Update to metrics-lib 2.15.0.
 
 
 # Changes in version 1.16.1 - 2020-08-16
diff --git a/build.xml b/build.xml
index 5dcb29d..f2bda4e 100644
--- a/build.xml
+++ b/build.xml
@@ -12,7 +12,7 @@
   
   
   
-  
+  
   
 
   
diff --git 
a/src/main/java/org/torproject/metrics/collector/persist/MicrodescriptorPersistence.java
 
b/src/main/java/org/torproject/metrics/collector/persist/MicrodescriptorPersistence.java
new file mode 100644
index 000..41929af
--- /dev/null
+++ 
b/src/main/java/org/torproject/metrics/collector/persist/MicrodescriptorPersistence.java
@@ -0,0 +1,36 @@
+/* Copyright 2020 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.metrics.collector.persist;
+
+import org.torproject.descriptor.Microdescriptor;
+import org.torproject.metrics.collector.conf.Annotation;
+
+import java.nio.file.Paths;
+
+public class MicrodescriptorPersistence
+extends DescriptorPersistence {
+
+  private static final String RELAY_DESCRIPTORS = "relay-descriptors";
+
+  public MicrodescriptorPersistence(Microdescriptor descriptor, long received,
+  String year, String month) {
+super(descriptor, Annotation.Microdescriptor.bytes());
+calculatePaths(received, year, month);
+  }
+
+  private void calculatePaths(long received, String year, String month) {
+String file = PersistenceUtils.dateTime(received);
+this.recentPath = Paths.get(
+RELAY_DESCRIPTORS, MICRODESCS, "micro",
+file + "-micro-" + year + "-" + month).toString();
+String digest = desc.getDigestSha256Hex();
+this.storagePath = Paths.get(
+RELAY_DESCRIPTORS,
+MICRODESC, year, month, "micro",
+digest.substring(0,1),
+digest.substring(1,2),
+digest).toString();
+  }
+}
+
diff --git 
a/src/main/java/org/torproject/metrics/collector/relaydescs/ArchiveWriter.java 
b/src/main/java/org/torproject/metrics/collector/relaydescs/ArchiveWriter.java
index 28472f8..5c58f23 100644
--- 
a/src/main/java/org/torproject/metrics/collector/relaydescs/ArchiveWriter.java
+++ 
b/src/main/java/org/torproject/metrics/collector/relaydescs/ArchiveWriter.java
@@ -7,6 +7,7 @@ import org.torproject.descriptor.BandwidthFile;
 import org.torproject.descriptor.Descriptor;
 import org.torproject.descriptor.DescriptorParser;
 import org.torproject.descriptor.DescriptorSourceFactory;
+import org.torproject.descriptor.Microdescriptor;
 import org.torproject.descriptor.RelayExtraInfoDescriptor;
 import org.torproject.descriptor.RelayNetworkStatusConsensus;
 import org.torproject.descriptor.RelayNetworkStatusVote;
@@ -109,6 +110,8 @@ public class ArchiveWriter extends CollecTorMain {
 this.mapPathDescriptors.put(
 "recent/relay-descriptors/microdescs/consensus-microdesc",
 RelayNetworkStatusConsensus.class);
+this.mapPathDescriptors.put("recent/relay-descriptors/microdescs/micro/",
+Microdescriptor.class);
 this.mapPathDescriptors.put("recent/relay-descriptors/server-descriptors",
 RelayServerDescriptor.class);
 this.mapPathDescriptors.put("recent/relay-descriptors/extra-infos",
@@ -801,15 +804,17 @@ public class ArchiveWriter extends CollecTorMain {
  * file written in the first call.  However, this method must be
  * called twice to store the same microdescriptor in two different
  * valid-after months. */
-SimpleDateFormat descriptorFormat = new SimpleDateFormat("/MM/");
+SimpleDateFormat descriptorFormat = new SimpleDateFormat("-MM");
+String[] yearMonth = descriptorFormat.format(validAfter).split("-");
 File tarballFile = Paths.get(this.outputDirectory, MICRODESC,
-descriptorFormat.format(validAfter), MICRO,
+yearMonth[0], yearMonth[1], MICRO,
 microdescriptorDigest.substring(0, 1),
 microdescriptorDigest.substring(1, 2),
 microdescriptorDigest).toFile();
 boolean tarballFileExistedBefore = tarballFile.exists(

[tor-commits] [collector/master] Add log statement for current memory usage.

2020-12-14 Thread karsten
commit bddc73ce6700a1498cbffb294de135949d02a41d
Author: Karsten Loesing 
Date:   Thu Dec 10 22:08:42 2020 +0100

Add log statement for current memory usage.
---
 .../org/torproject/metrics/collector/indexer/CreateIndexJson.java | 4 
 1 file changed, 4 insertions(+)

diff --git 
a/src/main/java/org/torproject/metrics/collector/indexer/CreateIndexJson.java 
b/src/main/java/org/torproject/metrics/collector/indexer/CreateIndexJson.java
index 3d86947..887a3e1 100644
--- 
a/src/main/java/org/torproject/metrics/collector/indexer/CreateIndexJson.java
+++ 
b/src/main/java/org/torproject/metrics/collector/indexer/CreateIndexJson.java
@@ -245,6 +245,10 @@ public class CreateIndexJson extends CollecTorMain {
   logger.info("Writing uncompressed and compressed index.json files to "
   + "disk.");
   this.writeIndex(this.index, now);
+  Runtime rt = Runtime.getRuntime();
+  logger.info("Current memory usage is: free = {} B, total = {} B, "
+  + "max = {} B.",
+  rt.freeMemory(), rt.totalMemory(), rt.maxMemory());
   logger.info("Pausing until next index update run.");
 } catch (IOException e) {
   logger.error("I/O error while updating index.json files. Trying again in 
"



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Correct issue number in change log.

2020-12-13 Thread karsten
commit 15ff2beed583768964c6c3e19ea48f5d4319286b
Author: Karsten Loesing 
Date:   Sun Dec 13 22:58:39 2020 +0100

Correct issue number in change log.
---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2655e01..5bce6f8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
 # Changes in version 0.9 - 2020-1?-??
 
  - Avoid tracebacks when visualizing analysis files containing only
-   unsuccessful measurements. Fixes #44012.
+   unsuccessful measurements. Fixes #40012.
 
 # Changes in version 0.8 - 2020-09-16
 

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Avoid tracebacks when visualizing measurements.

2020-12-13 Thread karsten
commit fe6ff0874dfd9b18bf5eb71c2394431ca44874bc
Author: Karsten Loesing 
Date:   Tue Nov 24 12:17:21 2020 +0100

Avoid tracebacks when visualizing measurements.

Attempting to visualize analysis files containing only unsuccessful
measurements results in various tracebacks.

With this patch we check more carefully whether a data frame is empty
before adding a plot.

Another related change is that we always include
"time_to_{first,last}_byte" and "mbps" columns in the CSV output,
regardless of whether there are there are any non-null values in the
data. See also #40004 for a previous related change.

And we check whether a Tor stream identifier exists before retrieving
a Tor stream.

Finally, we only include TTFB/TTLB if the usecs value is non-zero.

Fixes #44012.
---
 CHANGELOG.md   |  5 +
 onionperf/visualization.py | 37 +
 2 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c1f5ca..2655e01 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# Changes in version 0.9 - 2020-1?-??
+
+ - Avoid tracebacks when visualizing analysis files containing only
+   unsuccessful measurements. Fixes #44012.
+
 # Changes in version 0.8 - 2020-09-16
 
  - Add a new `onionperf filter` mode that takes an OnionPerf analysis
diff --git a/onionperf/visualization.py b/onionperf/visualization.py
index 2cd3161..75ec2b5 100644
--- a/onionperf/visualization.py
+++ b/onionperf/visualization.py
@@ -66,6 +66,7 @@ class TGenVisualization(Visualization):
 tgen_streams = analysis.get_tgen_streams(client)
 tgen_transfers = analysis.get_tgen_transfers(client)
 while tgen_streams or tgen_transfers:
+stream = {"time_to_first_byte": None, 
"time_to_last_byte": None, "error_code": None, "mbps": None}
 error_code = None
 source_port = None
 unix_ts_end = None
@@ -76,15 +77,15 @@ class TGenVisualization(Visualization):
 # that value with unit megabits per second.
 if tgen_streams:
 stream_id, stream_data = tgen_streams.popitem()
-stream = {"id": stream_id, "label": label,
-  "filesize_bytes": 
int(stream_data["stream_info"]["recvsize"]),
-  "error_code": None}
+stream["id"] = stream_id
+stream["label"] = label
+stream["filesize_bytes"] = 
int(stream_data["stream_info"]["recvsize"])
 stream["server"] = "onion" if ".onion:" in 
stream_data["transport_info"]["remote"] else "public"
 if "time_info" in stream_data:
 s = stream_data["time_info"]
-if "usecs-to-first-byte-recv" in s:
+if "usecs-to-first-byte-recv" in s and 
float(s["usecs-to-first-byte-recv"]) >= 0:
 stream["time_to_first_byte"] = 
float(s["usecs-to-first-byte-recv"])/100
-if "usecs-to-last-byte-recv" in s:
+if "usecs-to-last-byte-recv" in s and 
float(s["usecs-to-last-byte-recv"]) >= 0:
 stream["time_to_last_byte"] = 
float(s["usecs-to-last-byte-recv"])/100
 if "elapsed_seconds" in stream_data:
 s = stream_data["elapsed_seconds"]
@@ -100,9 +101,9 @@ class TGenVisualization(Visualization):
 stream["start"] = 
datetime.datetime.utcfromtimestamp(stream_data["unix_ts_start"])
 elif tgen_transfers:
 transfer_id, transfer_data = 
tgen_transfers.popitem()
-stream = {"id": transfer_id, "label": label,
-  "filesize_bytes": 
transfer_data["filesize_bytes"],
-  "error_code": None}
+stream["id"] = transfer_id
+stream["label"] = label
+stream["filesize_bytes"] = 
transfer_data["filesize_bytes

[tor-commits] [onionperf/develop] Merge branch 'task-40012' into develop

2020-12-13 Thread karsten
commit f8944174633f3b0fa5e74747086c02761f83ee70
Merge: ce36170 c46deb0
Author: Karsten Loesing 
Date:   Sun Dec 13 22:57:46 2020 +0100

Merge branch 'task-40012' into develop

 CHANGELOG.md   |  5 +
 onionperf/visualization.py | 42 --
 2 files changed, 33 insertions(+), 14 deletions(-)



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Move two data.empty checks after data.dropna calls.

2020-12-13 Thread karsten
commit c46deb00967899bdd9c15b9b7ee41ca5c53b
Author: Karsten Loesing 
Date:   Sun Dec 13 22:26:52 2020 +0100

Move two data.empty checks after data.dropna calls.

Suggested by acute, still related to #40012.
---
 onionperf/visualization.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/onionperf/visualization.py b/onionperf/visualization.py
index 75ec2b5..a793791 100644
--- a/onionperf/visualization.py
+++ b/onionperf/visualization.py
@@ -298,25 +298,26 @@ class TGenVisualization(Visualization):
 plt.close()
 
 def __draw_countplot(self, x, data, title, xlabel, ylabel, hue=None, 
hue_name=None):
+data = data.dropna(subset=[x])
 if data.empty:
 return
 plt.figure()
 if hue is not None:
 data = data.rename(columns={hue: hue_name})
-g = sns.countplot(data=data.dropna(subset=[x]), x=x, hue=hue_name)
+g = sns.countplot(data=data, x=x, hue=hue_name)
 g.set(xlabel=xlabel, ylabel=ylabel, title=title)
 sns.despine()
 self.page.savefig()
 plt.close()
 
 def __draw_stripplot(self, x, y, hue, hue_name, data, title, xlabel, 
ylabel):
+data = data.dropna(subset=[y])
 if data.empty:
 return
 plt.figure()
 data = data.rename(columns={hue: hue_name})
 xmin = data[x].min()
 xmax = data[x].max()
-data = data.dropna(subset=[y])
 g = sns.stripplot(data=data, x=x, y=y, hue=hue_name)
 g.set(title=title, xlabel=xlabel, ylabel=ylabel,
   xlim=(xmin - 0.03 * (xmax - xmin), xmax + 0.03 * (xmax - xmin)))



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Merge branch 'task-40008' into develop

2020-12-13 Thread karsten
commit ce36170f03b540fc4e2c8716a0bddade37196162
Merge: 118d149 0bf86a6
Author: Karsten Loesing 
Date:   Sun Dec 13 22:01:45 2020 +0100

Merge branch 'task-40008' into develop

 onionperf/onionperf | 3 +++
 1 file changed, 3 insertions(+)

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Log version number in each execution.

2020-12-13 Thread karsten
commit 0bf86a60854b567fd792728e1957dcf5107d1229
Author: Karsten Loesing 
Date:   Sun Dec 13 22:01:09 2020 +0100

Log version number in each execution.

Implements #40008.
---
 onionperf/onionperf | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/onionperf/onionperf b/onionperf/onionperf
index 032cd7d..90b5bf6 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -14,6 +14,8 @@ from socket import gethostname
 import onionperf.util as util
 from onionperf.monitor import get_supported_torctl_events
 
+__version__ = '0.8'
+
 DESC_MAIN = """
 OnionPerf is a utility to monitor, measure, analyze, and visualize the
 performance of Tor and Onion Services.
@@ -379,6 +381,7 @@ files generated by this script will be written""",
 sys.exit(1)
 else:
 args = main_parser.parse_args()
+logging.info("Starting OnionPerf version {0} in {1} 
mode.".format(__version__, sys.argv[1]))
 args.func(args)
 
 def monitor(args):



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [metrics-lib/master] Optimize parsing large files with many descriptors.

2020-12-11 Thread karsten
commit ff7e36c15626bdc24df54ebd94da5ab58f4de4c4
Author: Karsten Loesing 
Date:   Thu Dec 10 17:54:02 2020 +0100

Optimize parsing large files with many descriptors.

When parsing a large file with many descriptors we would repeatedly
search the remaining file for the sequence "newline + keyword + space"
and then "newline + keyword + newline" to find the start of the next
descriptor. However, if the keyword is always followed by newline, the
first search would always fail.

The optimization here is to search once whether the keyword is
followed by space or newline and avoid unnecessary searches when going
through the file.

In the long term we should use a better parser. But in the short term
this optimization will have a major impact on performance, in
particular with regard to concatenated microdescriptors.
---
 CHANGELOG.md   |  3 +++
 .../descriptor/impl/DescriptorParserImpl.java  | 27 ++
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8ff5723..828718d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@
- Parse version 3 onion service statistics contained in extra-info
  descriptors.
 
+ * Medium changes
+   - Optimize parsing of large files containing many descriptors.
+
 
 # Changes in version 2.14.0 - 2020-08-07
 
diff --git 
a/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java 
b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
index e008e7a..abe4411 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorParserImpl.java
@@ -181,16 +181,25 @@ public class DescriptorParserImpl implements 
DescriptorParser {
 String ascii = new String(rawDescriptorBytes, StandardCharsets.US_ASCII);
 boolean containsAnnotations = ascii.startsWith("@")
 || ascii.contains(NL + "@");
+boolean containsKeywordSpace = ascii.startsWith(key.keyword + SP)
+|| ascii.contains(NL + key.keyword + SP);
+boolean containsKeywordNewline = ascii.startsWith(key.keyword + NL)
+|| ascii.contains(NL + key.keyword + NL);
 while (startAnnotations < endAllDescriptors) {
-  int startDescriptor;
-  if (startAnnotations == ascii.indexOf(key.keyword + SP,
-  startAnnotations) || startAnnotations == ascii.indexOf(
-  key.keyword + NL)) {
+  int startDescriptor = -1;
+  if ((containsKeywordSpace
+  && startAnnotations == ascii.indexOf(key.keyword + SP,
+  startAnnotations))
+  || (containsKeywordNewline
+  && startAnnotations == ascii.indexOf(key.keyword + NL,
+  startAnnotations))) {
 startDescriptor = startAnnotations;
   } else {
-startDescriptor = ascii.indexOf(NL + key.keyword + SP,
-startAnnotations - 1);
-if (startDescriptor < 0) {
+if (containsKeywordSpace) {
+  startDescriptor = ascii.indexOf(NL + key.keyword + SP,
+  startAnnotations - 1);
+}
+if (startDescriptor < 0 && containsKeywordNewline) {
   startDescriptor = ascii.indexOf(NL + key.keyword + NL,
   startAnnotations - 1);
 }
@@ -204,10 +213,10 @@ public class DescriptorParserImpl implements 
DescriptorParser {
   if (containsAnnotations) {
 endDescriptor = ascii.indexOf(NL + "@", startDescriptor);
   }
-  if (endDescriptor < 0) {
+  if (endDescriptor < 0 && containsKeywordSpace) {
 endDescriptor = ascii.indexOf(NL + key.keyword + SP, startDescriptor);
   }
-  if (endDescriptor < 0) {
+  if (endDescriptor < 0 && containsKeywordNewline) {
 endDescriptor = ascii.indexOf(NL + key.keyword + NL, startDescriptor);
   }
   if (endDescriptor < 0) {



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [metrics-lib/master] Prepare for 2.15.0 release.

2020-12-11 Thread karsten
commit 07cab7f5604fe943c915c91251b8da322d53036c
Author: Karsten Loesing 
Date:   Fri Dec 11 14:50:18 2020 +0100

Prepare for 2.15.0 release.
---
 CHANGELOG.md | 4 +---
 build.xml| 2 +-
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bee22c3..cb5bce6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,8 @@
-# Changes in version 2.15.0 - 2020-??-??
+# Changes in version 2.15.0 - 2020-12-11
 
  * Medium changes
- Parse version 3 onion service statistics contained in extra-info
  descriptors.
-
- * Medium changes
- Optimize parsing of large files containing many descriptors.
 
  * Minor changes
diff --git a/build.xml b/build.xml
index 72990c6..1a9e555 100644
--- a/build.xml
+++ b/build.xml
@@ -7,7 +7,7 @@
 
 
-  
+  
   
   
   



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [metrics-lib/master] Parse version 3 onion service statistics lines.

2020-12-11 Thread karsten
commit ee7c3eb73824a84e33b6c44b60fa8a32f37ab71e
Author: Karsten Loesing 
Date:   Tue Dec 8 10:40:19 2020 +0100

Parse version 3 onion service statistics lines.

Implements the library part of tpo/metrics/statistics#40002.
---
 CHANGELOG.md   |  6 +-
 .../torproject/descriptor/ExtraInfoDescriptor.java | 55 +
 .../descriptor/impl/ExtraInfoDescriptorImpl.java   | 93 ++
 .../java/org/torproject/descriptor/impl/Key.java   |  3 +
 .../impl/ExtraInfoDescriptorImplTest.java  | 46 +++
 5 files changed, 202 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6886e8..8ff5723 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
-# Changes in version 2.??.? - 2020-??-??
+# Changes in version 2.15.0 - 2020-??-??
+
+ * Medium changes
+   - Parse version 3 onion service statistics contained in extra-info
+ descriptors.
 
 
 # Changes in version 2.14.0 - 2020-08-07
diff --git a/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java 
b/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
index e094fed..b553a22 100644
--- a/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
+++ b/src/main/java/org/torproject/descriptor/ExtraInfoDescriptor.java
@@ -674,6 +674,61 @@ public interface ExtraInfoDescriptor extends Descriptor {
*/
   Map getHidservDirOnionsSeenParameters();
 
+  /**
+   * Return the time in milliseconds since the epoch when the included version 
3
+   * onion service statistics interval ended, or -1 if no such statistics are
+   * included.
+   *
+   * @since 2.15.0
+   */
+  long getHidservV3StatsEndMillis();
+
+  /**
+   * Return the interval length of the included version 3 onion service
+   * statistics in seconds, or -1 if no such statistics are included.
+   *
+   * @since 2.15.0
+   */
+  long getHidservV3StatsIntervalLength();
+
+  /**
+   * Return the approximate number of RELAY cells seen in either direction on a
+   * version 3 onion service circuit after receiving and successfully 
processing
+   * a RENDEZVOUS1 cell, or null if no such statistics are included.
+   *
+   * @since 2.15.0
+   */
+  Double getHidservRendV3RelayedCells();
+
+  /**
+   * Return the obfuscation parameters applied to the original measurement 
value
+   * of RELAY cells seen in either direction on a version 3 onion service
+   * circuit after receiving and successfully processing a RENDEZVOUS1 cell, or
+   * null if no such statistics are included.
+   *
+   * @since 2.15.0
+   */
+  Map getHidservRendV3RelayedCellsParameters();
+
+  /**
+   * Return the approximate number of unique version 3 onion service identities
+   * seen in descriptors published to and accepted by this onion service
+   * directory, or null if no such statistics are included.
+   *
+   * @since 2.15.0
+   */
+  Double getHidservDirV3OnionsSeen();
+
+  /**
+   * Return the obfuscation parameters applied to the original measurement 
value
+   * of unique version 3 onion service identities seen in descriptors published
+   * to and accepted by this onion service directory, or null if no such
+   * statistics are included.
+   *
+   * @since 2.15.0
+   */
+  Map getHidservDirV3OnionsSeenParameters();
+
   /**
* Return the time in milliseconds since the epoch when the included
* padding-counts statistics ended, or -1 if no such statistics are included.
diff --git 
a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java 
b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
index 5cab6ab..4f87237 100644
--- a/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ExtraInfoDescriptorImpl.java
@@ -225,6 +225,15 @@ public abstract class ExtraInfoDescriptorImpl extends 
DescriptorImpl
 case HIDSERV_DIR_ONIONS_SEEN:
   this.parseHidservDirOnionsSeenLine(line, partsNoOpt);
   break;
+case HIDSERV_V3_STATS_END:
+  this.parseHidservV3StatsEndLine(line, partsNoOpt);
+  break;
+case HIDSERV_REND_V3_RELAYED_CELLS:
+  this.parseHidservRendV3RelayedCellsLine(line, partsNoOpt);
+  break;
+case HIDSERV_DIR_V3_ONIONS_SEEN:
+  this.parseHidservDirV3OnionsSeenLine(line, partsNoOpt);
+  break;
 case PADDING_COUNTS:
   this.parsePaddingCountsLine(line, partsNoOpt);
   break;
@@ -764,6 +773,46 @@ public abstract class ExtraInfoDescriptorImpl extends 
DescriptorImpl
 partsNoOpt, 2);
   }
 
+  private void parseHidservV3StatsEndLine(String line,
+  String[] partsNoOpt) throws DescriptorParseException {
+long[] parsedStatsEndData = this.parseStatsEndLine(line, partsNoOpt,
+5);
+this.hidservV3StatsEndMillis = parsedStatsEndData[0];
+this.hidservV3StatsIntervalLength = parsedStatsEndData[1];
+  }
+
+  private void

[tor-commits] [metrics-lib/master] Bump version to 2.15.0-dev.

2020-12-11 Thread karsten
commit 179d47ee259ffe86302e57dad6cbc39fdbb72f0b
Author: Karsten Loesing 
Date:   Fri Dec 11 14:53:56 2020 +0100

Bump version to 2.15.0-dev.
---
 CHANGELOG.md | 3 +++
 build.xml| 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb5bce6..ac865a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+# Changes in version 2.??.? - 2020-??-??
+
+
 # Changes in version 2.15.0 - 2020-12-11
 
  * Medium changes
diff --git a/build.xml b/build.xml
index 1a9e555..7544a32 100644
--- a/build.xml
+++ b/build.xml
@@ -7,7 +7,7 @@
 
 
-  
+  
   
   
   

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [metrics-lib/master] Provide microdescriptor digest in hex encoding.

2020-12-11 Thread karsten
commit 664921eb50491a774a5281bf1c12836ab7dedd94
Author: Karsten Loesing 
Date:   Fri Dec 11 10:22:39 2020 +0100

Provide microdescriptor digest in hex encoding.
---
 CHANGELOG.md |  3 +++
 .../java/org/torproject/descriptor/Microdescriptor.java  |  9 +
 .../torproject/descriptor/impl/MicrodescriptorImpl.java  | 16 
 .../descriptor/impl/MicrodescriptorImplTest.java |  3 +++
 4 files changed, 31 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 828718d..bee22c3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,9 @@
  * Medium changes
- Optimize parsing of large files containing many descriptors.
 
+ * Minor changes
+   - Provide microdescriptor SHA-256 digest in hexadecimal encoding.
+
 
 # Changes in version 2.14.0 - 2020-08-07
 
diff --git a/src/main/java/org/torproject/descriptor/Microdescriptor.java 
b/src/main/java/org/torproject/descriptor/Microdescriptor.java
index feaf00b..0d329b7 100644
--- a/src/main/java/org/torproject/descriptor/Microdescriptor.java
+++ b/src/main/java/org/torproject/descriptor/Microdescriptor.java
@@ -32,6 +32,15 @@ public interface Microdescriptor extends Descriptor {
*/
   String getDigestSha256Base64();
 
+  /**
+   * Return the SHA-256 descriptor digest, encoded as 64 lower-case hexadecimal
+   * characters, that can be used as file name when writing this descriptor to
+   * disk.
+   *
+   * @since 2.15.0
+   */
+  String getDigestSha256Hex();
+
   /**
* Return the RSA-1024 public key in PEM format used to encrypt CREATE
* cells for this server, or null if the descriptor doesn't contain an
diff --git 
a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java 
b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
index 87ab7ae..8d4ac1b 100644
--- a/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/MicrodescriptorImpl.java
@@ -6,6 +6,9 @@ package org.torproject.descriptor.impl;
 import org.torproject.descriptor.DescriptorParseException;
 import org.torproject.descriptor.Microdescriptor;
 
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -26,6 +29,7 @@ public class MicrodescriptorImpl extends DescriptorImpl
 super(descriptorBytes, offsetAndLength, descriptorFile, false);
 this.parseDescriptorBytes();
 this.calculateDigestSha256Base64(Key.ONION_KEY.keyword + NL);
+this.convertDigestSha256Base64ToHex();
 this.checkExactlyOnceKeys(EnumSet.of(Key.ONION_KEY));
 Set atMostOnceKeys = EnumSet.of(
 Key.NTOR_ONION_KEY, Key.FAMILY, Key.P, Key.P6, Key.ID);
@@ -212,6 +216,18 @@ public class MicrodescriptorImpl extends DescriptorImpl
 }
   }
 
+  private void convertDigestSha256Base64ToHex() {
+this.digestSha256Hex = Hex.encodeHexString(Base64.decodeBase64(
+this.getDigestSha256Base64()));
+  }
+
+  private String digestSha256Hex;
+
+  @Override
+  public String getDigestSha256Hex() {
+return this.digestSha256Hex;
+  }
+
   private String onionKey;
 
   @Override
diff --git 
a/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java 
b/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
index 128d39a..fbc2fc9 100644
--- a/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/MicrodescriptorImplTest.java
@@ -74,6 +74,9 @@ public class MicrodescriptorImplTest {
 Microdescriptor micro = DescriptorBuilder.createWithDefaultLines();
 assertEquals("ER1AC4KqT//o3pJDrqlmej5G2qW1EQYEr/IrMQHNc6I",
 micro.getDigestSha256Base64());
+assertEquals(
+"111d400b82aa4fffe8de9243aea9667a3e46daa5b5110604aff22b3101cd73a2",
+micro.getDigestSha256Hex());
   }
 
   @Test



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [collector/master] Use persist package for writing bridge descriptors.

2020-12-02 Thread karsten
commit 7439fa933c9b686a8650d0b9d5c398fdd434f749
Author: Karsten Loesing 
Date:   Tue Dec 1 22:32:46 2020 +0100

Use persist package for writing bridge descriptors.

Implements #25307.
---
 .../bridgedescs/SanitizedBridgesWriter.java| 104 +++--
 .../BridgeExtraInfoDescriptorPersistence.java  |  54 +++
 .../persist/BridgeExtraInfoPersistence.java|  39 
 .../persist/BridgeNetworkStatusPersistence.java|  56 +++
 .../persist/BridgeServerDescriptorPersistence.java |  43 ++---
 .../collector/persist/DescriptorPersistence.java   |  40 +---
 .../collector/persist/StatusPersistence.java   |  41 
 .../metrics/collector/sync/SyncPersistence.java|  10 +-
 .../bridgedescs/SanitizedBridgesWriterTest.java|   2 +-
 9 files changed, 188 insertions(+), 201 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
index 5e24f5d..7619453 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
@@ -10,6 +10,9 @@ import org.torproject.metrics.collector.conf.Configuration;
 import org.torproject.metrics.collector.conf.ConfigurationException;
 import org.torproject.metrics.collector.conf.Key;
 import org.torproject.metrics.collector.cron.CollecTorMain;
+import 
org.torproject.metrics.collector.persist.BridgeExtraInfoDescriptorPersistence;
+import org.torproject.metrics.collector.persist.BridgeNetworkStatusPersistence;
+import 
org.torproject.metrics.collector.persist.BridgeServerDescriptorPersistence;
 import org.torproject.metrics.collector.persist.PersistenceUtils;
 
 import org.apache.commons.codec.binary.Hex;
@@ -28,8 +31,6 @@ import java.io.StringReader;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
@@ -57,7 +58,6 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 
   private static final Logger logger = LoggerFactory.getLogger(
   SanitizedBridgesWriter.class);
-  private static final String BRIDGE_DESCRIPTORS = "bridge-descriptors";
 
   /** Initialize configuration. */
   public SanitizedBridgesWriter(Configuration config) {
@@ -91,10 +91,8 @@ public class SanitizedBridgesWriter extends CollecTorMain {
   @Override
   protected void startProcessing() throws ConfigurationException {
 
-this.outputDirectory = config.getPath(Key.OutputPath)
-.resolve(BRIDGE_DESCRIPTORS);
-this.recentDirectory = config.getPath(Key.RecentPath)
-.resolve(BRIDGE_DESCRIPTORS);
+this.outputDirectory = config.getPath(Key.OutputPath);
+this.recentDirectory = config.getPath(Key.RecentPath);
 Path inputDirectory = config.getPath(Key.BridgeLocalOrigins);
 Path statsDirectory = config.getPath(Key.StatsPath);
 boolean replaceIpAddressesWithHashes =
@@ -352,27 +350,9 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 || publicationTime.compareTo(maxNetworkStatusPublishedTime) > 0) {
   maxNetworkStatusPublishedTime = publicationTime;
 }
-try {
-  String syear = publicationTime.substring(0, 4);
-  String smonth = publicationTime.substring(5, 7);
-  String sday = publicationTime.substring(8, 10);
-  String stime = publicationTime.substring(11, 13)
-  + publicationTime.substring(14, 16)
-  + publicationTime.substring(17, 19);
-  String fileName = syear + smonth + sday + "-" + stime + "-"
-  + authorityFingerprint;
-  Path tarballFile = this.outputDirectory.resolve(
-  Paths.get(syear, smonth, "statuses", sday, fileName));
-  Path rsyncFile = this.recentDirectory.resolve(
-  Paths.get("statuses", fileName));
-  for (Path outputFile : new Path[] { tarballFile, rsyncFile }) {
-Files.createDirectories(outputFile.getParent());
-Files.write(outputFile, scrubbedBytes);
-  }
-} catch (IOException e) {
-  logger.warn("Could not write sanitized bridge "
-  + "network status to disk.", e);
-}
+new BridgeNetworkStatusPersistence(scrubbedBytes, publicationTime,
+authorityFingerprint)
+.storeAll(this.recentDirectory, this.outputDirectory);
   }
 
   private String maxServerDescriptorPublishedTime = null;
@@ -398,36 +378,9 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 }
 String descriptorDigest
 = sanitizedBridgeServerDescriptor.getDescriptorDigest();
-
-/* Determine filename of sanitized server descriptor. */
-String dyear =

[tor-commits] [collector/master] Use java8 datetime classes in bridgedesc module.

2020-12-01 Thread karsten
commit 27a21c0b37d504b4ab3eeb5b206b5590fc82d396
Author: Karsten Loesing 
Date:   Tue Dec 1 11:15:21 2020 +0100

Use java8 datetime classes in bridgedesc module.

Implements #25309.
---
 .../bridgedescs/SanitizedBridgeNetworkStatus.java  | 31 ++-
 .../bridgedescs/SanitizedBridgesWriter.java| 64 +++---
 .../BridgePoolAssignmentsProcessor.java|  8 +--
 3 files changed, 51 insertions(+), 52 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgeNetworkStatus.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgeNetworkStatus.java
index d94cb0d..8087e84 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgeNetworkStatus.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgeNetworkStatus.java
@@ -15,8 +15,8 @@ import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.StringReader;
 import java.nio.charset.StandardCharsets;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
@@ -186,24 +186,25 @@ public class SanitizedBridgeNetworkStatus extends 
SanitizedBridgeDescriptor {
 
   /* Check if we can tell from the descriptor publication times
* whether this status is possibly stale. */
-  SimpleDateFormat formatter = new SimpleDateFormat(
-  "-MM-dd HH:mm:ss");
+  DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
+  "-MM-dd HH:mm:ss");
   if (null == mostRecentDescPublished) {
 logger.warn("The bridge network status published at {}"
 + " does not contain a single entry. Please ask the bridge "
 + "authority operator to check!", this.publishedString);
-  } else if (formatter.parse(this.publishedString).getTime()
-  - formatter.parse(mostRecentDescPublished).getTime()
-  > 60L * 60L * 1000L) {
-logger.warn("The most recent descriptor in the bridge "
-+ "network status published at {} was published at {} which is 
"
-+ "more than 1 hour before the status. This is a sign for "
-+ "the status being stale. Please check!",
-this.publishedString, mostRecentDescPublished);
+  } else {
+LocalDateTime networkStatusTime
+= LocalDateTime.parse(this.publishedString, formatter);
+LocalDateTime mostRecentDescTime
+= LocalDateTime.parse(mostRecentDescPublished, formatter);
+if (mostRecentDescTime.isBefore(networkStatusTime.minusHours(1L))) {
+  logger.warn("The most recent descriptor in the bridge "
+  + "network status published at {} was published at {} which is "
+  + "more than 1 hour before the status. This is a sign for "
+  + "the status being stale. Please check!",
+  this.publishedString, mostRecentDescPublished);
+}
   }
-} catch (ParseException e) {
-  logger.warn("Could not parse timestamp in bridge network status.", e);
-  return false;
 } catch (IOException e) {
   logger.warn("Could not parse bridge network status.", e);
   return false;
diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
index d5009e1..5e24f5d 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
@@ -30,9 +30,9 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
 import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.HashSet;
 import java.util.Set;
@@ -99,10 +99,9 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 Path statsDirectory = config.getPath(Key.StatsPath);
 boolean replaceIpAddressesWithHashes =
 config.getBool(Key.ReplaceIpAddressesWithHashes);
-SimpleDateFormat rsyncCatFormat = new SimpleDateFormat(
-"-MM-dd-HH-mm-ss");
-this.rsyncCatString = rsyncCatFormat.format(
-System.currentTimeMillis());
+DateTimeFormatter rsyncCatFormat = DateTimeFormatter.ofPattern(
+"-MM-dd-HH-mm-ss");
+this.rsyncCatString = LocalDateTime.now().format(rsyncCatFormat);
 
 Path bridgeIpSecr

[tor-commits] [collector/master] Move sanitizing code to one class per type.

2020-12-01 Thread karsten
commit 2e8cdf7fe1cd11b6afe599512e4844c4234e257a
Author: Karsten Loesing 
Date:   Tue Dec 1 10:35:26 2020 +0100

Move sanitizing code to one class per type.

Part of #20542.
---
 .../bridgedescs/SanitizedBridgeDescriptor.java | 118 
 .../SanitizedBridgeExtraInfoDescriptor.java| 192 +
 .../bridgedescs/SanitizedBridgeNetworkStatus.java  | 230 ++
 .../SanitizedBridgeServerDescriptor.java   | 360 ++
 .../bridgedescs/SanitizedBridgesWriter.java| 771 +
 5 files changed, 934 insertions(+), 737 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgeDescriptor.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgeDescriptor.java
new file mode 100644
index 000..5ddeefe
--- /dev/null
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgeDescriptor.java
@@ -0,0 +1,118 @@
+/* Copyright 2010--2020 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.metrics.collector.bridgedescs;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+
+public abstract class SanitizedBridgeDescriptor {
+
+  private static final Logger logger = LoggerFactory.getLogger(
+  SanitizedBridgeDescriptor.class);
+
+  protected byte[] originalBytes;
+
+  protected SensitivePartsSanitizer sensitivePartsSanitizer;
+
+  protected byte[] sanitizedBytes;
+
+  protected String publishedString;
+
+  SanitizedBridgeDescriptor(byte[] originalBytes,
+  SensitivePartsSanitizer sensitivePartsSanitizer) {
+this.originalBytes = originalBytes;
+this.sensitivePartsSanitizer = sensitivePartsSanitizer;
+  }
+
+  protected String parseMasterKeyEd25519FromIdentityEd25519(
+  String identityEd25519Base64) {
+byte[] identityEd25519 = Base64.decodeBase64(identityEd25519Base64);
+if (identityEd25519.length < 40) {
+  logger.warn("Invalid length of identity-ed25519 (in bytes): {}",
+  identityEd25519.length);
+} else if (identityEd25519[0] != 0x01) {
+  logger.warn("Unknown version in identity-ed25519: {}",
+  identityEd25519[0]);
+} else if (identityEd25519[1] != 0x04) {
+  logger.warn("Unknown cert type in identity-ed25519: {}",
+  identityEd25519[1]);
+} else if (identityEd25519[6] != 0x01) {
+  logger.warn("Unknown certified key type in identity-ed25519: {}",
+  identityEd25519[1]);
+} else if (identityEd25519[39] == 0x00) {
+  logger.warn("No extensions in identity-ed25519 (which "
+  + "would contain the encoded master-key-ed25519): {}",
+  identityEd25519[39]);
+} else {
+  int extensionStart = 40;
+  for (int i = 0; i < (int) identityEd25519[39]; i++) {
+if (identityEd25519.length < extensionStart + 4) {
+  logger.warn("Invalid extension with id {} in identity-ed25519.", i);
+  break;
+}
+int extensionLength = identityEd25519[extensionStart];
+extensionLength <<= 8;
+extensionLength += identityEd25519[extensionStart + 1];
+int extensionType = identityEd25519[extensionStart + 2];
+if (extensionLength == 32 && extensionType == 4) {
+  if (identityEd25519.length < extensionStart + 4 + 32) {
+logger.warn("Invalid extension with id {} in identity-ed25519.", 
i);
+break;
+  }
+  byte[] masterKeyEd25519 = new byte[32];
+  System.arraycopy(identityEd25519, extensionStart + 4,
+  masterKeyEd25519, 0, masterKeyEd25519.length);
+  String masterKeyEd25519Base64 = Base64.encodeBase64String(
+  masterKeyEd25519);
+  return masterKeyEd25519Base64.replaceAll("=", "");
+}
+extensionStart += 4 + extensionLength;
+  }
+}
+logger.warn("Unable to locate master-key-ed25519 in identity-ed25519.");
+return null;
+  }
+
+  protected String computeDescriptorDigest(byte[] descriptorBytes,
+  String startToken, String sigToken) {
+String descriptorDigest = null;
+String ascii = new String(descriptorBytes, StandardCharsets.US_ASCII);
+int start = ascii.indexOf(startToken);
+int sig = ascii.indexOf(sigToken) + sigToken.length();
+if (start >= 0 && sig >= 0 && sig > start) {
+  byte[] forDigest = new byte[sig - start];
+  System.arraycopy(descriptorBytes, start, forDigest, 0, sig - start);
+  descriptorDigest = DigestUtils.sha1Hex(DigestUtils.sha1(forDigest));
+}
+if (descriptorDigest == null) {
+  logger.warn("Could not calculate extra-info descriptor digest.");

[tor-commits] [collector/master] Move lower-level sanitizing code to its own class.

2020-12-01 Thread karsten
commit a2fdbf3c6f67e5ddb735773e1ab456ee4f464555
Author: Karsten Loesing 
Date:   Mon Nov 30 21:59:17 2020 +0100

Move lower-level sanitizing code to its own class.

Part of #20542.
---
 .../bridgedescs/SanitizedBridgesWriter.java| 404 ++---
 .../bridgedescs/SensitivePartsSanitizer.java   | 378 +++
 .../bridgedescs/SanitizedBridgesWriterTest.java|   2 +
 3 files changed, 410 insertions(+), 374 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
index 34156c2..843aa40 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
@@ -3,8 +3,6 @@
 
 package org.torproject.metrics.collector.bridgedescs;
 
-import static java.time.ZoneOffset.UTC;
-
 import org.torproject.descriptor.BridgeExtraInfoDescriptor;
 import org.torproject.descriptor.BridgeNetworkStatus;
 import org.torproject.descriptor.BridgeServerDescriptor;
@@ -35,18 +33,12 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
-import java.security.GeneralSecurityException;
-import java.security.SecureRandom;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.SortedMap;
@@ -89,26 +81,14 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 
   private Path inputDirectory;
 
-  private boolean replaceIpAddressesWithHashes;
-
-  private boolean persistenceProblemWithSecrets;
-
-  private SortedMap secretsForHashingIpAddresses;
-
-  private String bridgeSanitizingCutOffTimestamp;
-
-  private boolean haveWarnedAboutInterval;
-
-  private Path bridgeIpSecretsFile;
-
-  private SecureRandom secureRandom;
-
   private Path outputDirectory;
 
   private Path recentDirectory;
 
   private Path statsDirectory;
 
+  private SensitivePartsSanitizer sensitivePartsSanitizer;
+
   @Override
   public String module() {
 return "bridgedescs";
@@ -128,90 +108,30 @@ public class SanitizedBridgesWriter extends CollecTorMain 
{
 .resolve(BRIDGE_DESCRIPTORS);
 this.inputDirectory = config.getPath(Key.BridgeLocalOrigins);
 this.statsDirectory = config.getPath(Key.StatsPath);
-this.replaceIpAddressesWithHashes =
+boolean replaceIpAddressesWithHashes =
 config.getBool(Key.ReplaceIpAddressesWithHashes);
 SimpleDateFormat rsyncCatFormat = new SimpleDateFormat(
 "-MM-dd-HH-mm-ss");
 this.rsyncCatString = rsyncCatFormat.format(
 System.currentTimeMillis());
 
-/* Initialize secure random number generator if we need it. */
-if (this.replaceIpAddressesWithHashes) {
-  try {
-this.secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
-  } catch (GeneralSecurityException e) {
-logger.warn("Could not initialize secure "
-+ "random number generator! Not calculating any IP address "
-+ "hashes in this execution!", e);
-this.persistenceProblemWithSecrets = true;
-  }
-}
-
-/* Read hex-encoded secrets for replacing IP addresses with hashes
- * from disk. */
-this.secretsForHashingIpAddresses = new TreeMap<>();
-this.bridgeIpSecretsFile = statsDirectory.resolve("bridge-ip-secrets");
-if (Files.exists(this.bridgeIpSecretsFile)) {
-  try {
-for (String line : Files.readAllLines(this.bridgeIpSecretsFile)) {
-  String[] parts = line.split(",");
-  if ((line.length() != ("-MM,".length() + 31 * 2)
-  && line.length() != ("-MM,".length() + 50 * 2)
-  && line.length() != ("-MM,".length() + 83 * 2))
-  || parts.length != 2) {
-logger.warn("Invalid line in bridge-ip-secrets file "
-+ "starting with '{}'! "
-+ "Not calculating any IP address hashes in this "
-+ "execution!", line.substring(0, 7));
-this.persistenceProblemWithSecrets = true;
-break;
-  }
-  String month = parts[0];
-  byte[] secret = Hex.decodeHex(parts[1].toCharArray());
-  this.secretsForHashingIpAddresses.put(month, secret);
-}
-if (!this.persistenceProblemWithSecrets) {
-  logger.debug("Read {} secrets for 

[tor-commits] [collector/master] Simplify the bridgedescs module.

2020-12-01 Thread karsten
commit 106852425554f6001f114ee711648798c78609ec
Author: Karsten Loesing 
Date:   Sat Nov 28 22:14:53 2020 +0100

Simplify the bridgedescs module.

The separation between BridgeSnapshotReader, BridgeDescriptorParser,
and SanitizedBridgesWriter doesn't make much sense anymore:

 - BridgeSnapshotReader only has a constructor of more than 200 lines
   of code.

 - BridgeDescriptorParser actually only determines the descriptor type
   and

 - SanitizedBridgesWriter performs parsing and obfuscation.

There are better ways to structure this code. The first step in that
direction is to remove clutter by moving the code to read bridge
snapshots to SanitizedBridgesWriter and deleting the other two
classes.

Part of #20542.
---
 .../bridgedescs/BridgeDescriptorParser.java|  55 -
 .../bridgedescs/BridgeSnapshotReader.java  | 248 -
 .../bridgedescs/SanitizedBridgesWriter.java| 228 ++-
 .../bridgedescs/BridgeDescriptorParserTest.java|  43 
 4 files changed, 223 insertions(+), 351 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/BridgeDescriptorParser.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/BridgeDescriptorParser.java
deleted file mode 100644
index b5e30bc..000
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/BridgeDescriptorParser.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2010--2020 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.metrics.collector.bridgedescs;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.nio.charset.StandardCharsets;
-
-public class BridgeDescriptorParser {
-
-  private SanitizedBridgesWriter sbw;
-
-  private static final Logger logger = LoggerFactory.getLogger(
-  BridgeDescriptorParser.class);
-
-  /** Initializes a new bridge descriptor parser and links it to a
-   * sanitized bridges writer to sanitize and store bridge descriptors. */
-  public BridgeDescriptorParser(SanitizedBridgesWriter sbw) {
-if (null == sbw) {
-  throw new IllegalArgumentException("SanitizedBridgesWriter has to be "
-  + "provided, but was null.");
-}
-this.sbw = sbw;
-  }
-
-  /** Parses the first line of the given descriptor data to determine the
-   * descriptor type and passes it to the sanitized bridges writer. */
-  public void parse(byte[] allData, String dateTime,
-  String authorityFingerprint) {
-try {
-  BufferedReader br = new BufferedReader(new StringReader(
-  new String(allData, StandardCharsets.US_ASCII)));
-  String line = br.readLine();
-  if (line == null) {
-return;
-  }
-  if (line.startsWith("router ")) {
-this.sbw.sanitizeAndStoreServerDescriptor(allData);
-  } else if (line.startsWith("extra-info ")) {
-this.sbw.sanitizeAndStoreExtraInfoDescriptor(allData);
-  } else {
-this.sbw.sanitizeAndStoreNetworkStatus(allData, dateTime,
-authorityFingerprint);
-  }
-} catch (IOException e) {
-  logger.warn("Could not parse or write bridge descriptor.", e);
-}
-  }
-}
-
diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/BridgeSnapshotReader.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/BridgeSnapshotReader.java
deleted file mode 100644
index de9cd4b..000
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/BridgeSnapshotReader.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/* Copyright 2010--2020 The Tor Project
- * See LICENSE for licensing information */
-
-package org.torproject.metrics.collector.bridgedescs;
-
-import org.apache.commons.codec.binary.Hex;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
-import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.StringReader;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.Stack;
-import java.util.TreeSet;
-
-public class BridgeSnapshotReader {
-
-  private static final Logger logger = LoggerFactory.getLogger(
-  BridgeSnapshotReader.class);
-
-  /**
-   * Reads the half-hourly snapshots of bridge descriptors from Bifroest.
-   */
-  public BridgeSnap

[tor-commits] [collector/master] Make some minor optimizations to bridgedescs code.

2020-12-01 Thread karsten
commit 47a4c7a962de55ee8354c1c8605216965f68d116
Author: Karsten Loesing 
Date:   Mon Nov 30 22:45:22 2020 +0100

Make some minor optimizations to bridgedescs code.

Part of #20542.
---
 .../collector/bridgedescs/DescriptorBuilder.java   |  3 ++
 .../bridgedescs/SanitizedBridgesWriter.java| 62 ++
 2 files changed, 32 insertions(+), 33 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/DescriptorBuilder.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/DescriptorBuilder.java
index b4b63e7..946fcdb 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/DescriptorBuilder.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/DescriptorBuilder.java
@@ -96,4 +96,7 @@ class DescriptorBuilder {
 return value;
   }
 
+  public byte[] toBytes() {
+return this.toString().getBytes();
+  }
 }
diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
index 843aa40..77ab406 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
@@ -79,14 +79,10 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 
   private String rsyncCatString;
 
-  private Path inputDirectory;
-
   private Path outputDirectory;
 
   private Path recentDirectory;
 
-  private Path statsDirectory;
-
   private SensitivePartsSanitizer sensitivePartsSanitizer;
 
   @Override
@@ -106,8 +102,8 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 .resolve(BRIDGE_DESCRIPTORS);
 this.recentDirectory = config.getPath(Key.RecentPath)
 .resolve(BRIDGE_DESCRIPTORS);
-this.inputDirectory = config.getPath(Key.BridgeLocalOrigins);
-this.statsDirectory = config.getPath(Key.StatsPath);
+Path inputDirectory = config.getPath(Key.BridgeLocalOrigins);
+Path statsDirectory = config.getPath(Key.StatsPath);
 boolean replaceIpAddressesWithHashes =
 config.getBool(Key.ReplaceIpAddressesWithHashes);
 SimpleDateFormat rsyncCatFormat = new SimpleDateFormat(
@@ -126,7 +122,7 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 }
 
 // Import bridge descriptors
-this.readBridgeSnapshots(this.inputDirectory, this.statsDirectory);
+this.readBridgeSnapshots(inputDirectory, statsDirectory);
 
 // Finish writing sanitized bridge descriptors to disk
 if (replaceIpAddressesWithHashes) {
@@ -362,11 +358,16 @@ public class SanitizedBridgesWriter extends CollecTorMain 
{
 }
 
 /* Parse the given network status line by line. */
-DescriptorBuilder header = new DescriptorBuilder();
 boolean includesFingerprintLine = false;
-SortedMap scrubbedLines = new TreeMap<>();
+DescriptorBuilder scrubbed = new DescriptorBuilder();
+scrubbed.append(Annotation.Status.toString());
+SortedMap scrubbedEntries = new TreeMap<>();
+StringBuilder publishedStringBuilder = new StringBuilder();
+scrubbed.append("published ").append(publishedStringBuilder).newLine();
+DescriptorBuilder header = new DescriptorBuilder();
+scrubbed.append(header);
+
 try {
-  DescriptorBuilder scrubbed = new DescriptorBuilder();
   BufferedReader br = new BufferedReader(new StringReader(new String(
   data, StandardCharsets.US_ASCII)));
   String line;
@@ -374,6 +375,7 @@ public class SanitizedBridgesWriter extends CollecTorMain {
   byte[] fingerprintBytes = null;
   String descPublicationTime = null;
   String hashedBridgeIdentityHex = null;
+  DescriptorBuilder scrubbedEntry = new DescriptorBuilder();
   while ((line = br.readLine()) != null) {
 
 /* Use publication time from "published" line instead of the
@@ -403,10 +405,10 @@ public class SanitizedBridgesWriter extends CollecTorMain 
{
 } else if (line.startsWith("r ")) {
 
   /* Clear buffer from previously scrubbed lines. */
-  if (scrubbed.hasContent()) {
-String scrubbedLine = scrubbed.toString();
-scrubbedLines.put(hashedBridgeIdentityHex, scrubbedLine);
-scrubbed = new DescriptorBuilder();
+  if (scrubbedEntry.hasContent()) {
+scrubbedEntries.put(hashedBridgeIdentityHex,
+scrubbedEntry.toString());
+scrubbedEntry = new DescriptorBuilder();
   }
 
   /* Parse the relevant parts of this r line. */
@@ -452,7 +454,7 @@ public class SanitizedBridgesWriter extends CollecTorMain {
   orPort, fingerprintBytes, descPublicationTime);
   String scrubbedDirPort = this.sensitivePartsSanitizer.scrubTcpPort(
   dirPort, fingerprintBytes, descPublicationTime);
-  sc

[tor-commits] [collector/master] Update most of the bridgedescs module to NIO.

2020-12-01 Thread karsten
commit c0ee1a6cf76f6f6b1677edccc1dc7e4055de50e9
Author: Karsten Loesing 
Date:   Sun Nov 29 00:05:47 2020 +0100

Update most of the bridgedescs module to NIO.

Replace all File references with their Path equivalents, and use Files
methods wherever feasible.

Part of #20542.
---
 .../bridgedescs/SanitizedBridgesWriter.java| 288 +
 1 file changed, 127 insertions(+), 161 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
index 8db7db5..34156c2 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
@@ -26,16 +26,15 @@ import org.slf4j.LoggerFactory;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedReader;
-import java.io.BufferedWriter;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.StringReader;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
 import java.security.GeneralSecurityException;
 import java.security.SecureRandom;
 import java.text.ParseException;
@@ -45,7 +44,6 @@ import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -89,12 +87,7 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 
   private String rsyncCatString;
 
-  private File bridgeDirectoriesDirectory;
-
-  /**
-   * Output directory for writing sanitized bridge descriptors.
-   */
-  private File sanitizedBridgesDirectory;
+  private Path inputDirectory;
 
   private boolean replaceIpAddressesWithHashes;
 
@@ -106,13 +99,15 @@ public class SanitizedBridgesWriter extends CollecTorMain {
 
   private boolean haveWarnedAboutInterval;
 
-  private File bridgeIpSecretsFile;
+  private Path bridgeIpSecretsFile;
 
   private SecureRandom secureRandom;
 
-  private String outputPathName;
+  private Path outputDirectory;
+
+  private Path recentDirectory;
 
-  private String recentPathName;
+  private Path statsDirectory;
 
   @Override
   public String module() {
@@ -127,25 +122,12 @@ public class SanitizedBridgesWriter extends CollecTorMain 
{
   @Override
   protected void startProcessing() throws ConfigurationException {
 
-outputPathName = Paths.get(config.getPath(Key.OutputPath).toString(),
-BRIDGE_DESCRIPTORS).toString();
-recentPathName = Paths.get(config.getPath(Key.RecentPath).toString(),
-BRIDGE_DESCRIPTORS).toString();
-File bridgeDirectoriesDirectory =
-config.getPath(Key.BridgeLocalOrigins).toFile();
-File sanitizedBridgesDirectory = new File(outputPathName);
-File statsDirectory = config.getPath(Key.StatsPath).toFile();
-
-if (bridgeDirectoriesDirectory == null
-|| sanitizedBridgesDirectory == null || statsDirectory == null) {
-  throw new ConfigurationException("BridgeSnapshotsDirectory, "
-  + "SanitizedBridgesWriteDirectory, StatsPath should be set. "
-  + "Please, edit the 'collector.properties' file.");
-}
-
-/* Memorize argument values. */
-this.bridgeDirectoriesDirectory = bridgeDirectoriesDirectory;
-this.sanitizedBridgesDirectory = sanitizedBridgesDirectory;
+this.outputDirectory = config.getPath(Key.OutputPath)
+.resolve(BRIDGE_DESCRIPTORS);
+this.recentDirectory = config.getPath(Key.RecentPath)
+.resolve(BRIDGE_DESCRIPTORS);
+this.inputDirectory = config.getPath(Key.BridgeLocalOrigins);
+this.statsDirectory = config.getPath(Key.StatsPath);
 this.replaceIpAddressesWithHashes =
 config.getBool(Key.ReplaceIpAddressesWithHashes);
 SimpleDateFormat rsyncCatFormat = new SimpleDateFormat(
@@ -168,13 +150,10 @@ public class SanitizedBridgesWriter extends CollecTorMain 
{
 /* Read hex-encoded secrets for replacing IP addresses with hashes
  * from disk. */
 this.secretsForHashingIpAddresses = new TreeMap<>();
-this.bridgeIpSecretsFile = new File(statsDirectory,
-"bridge-ip-secrets");
-if (this.bridgeIpSecretsFile.exists()) {
-  try (BufferedReader br = new BufferedReader(new FileReader(
-  this.bridgeIpSecretsFile))) {
-String line;
-while ((line = br.readLine()) != null) {
+this.bridgeIpSecretsFile = statsDirectory.resolve("bridge-ip-secrets");
+if (Files.exists(this.bridgeIpSecretsFile)) {
+  

[tor-commits] [collector/master] Add change log entry for #34030 fix.

2020-11-30 Thread karsten
commit f1c7198ac4a414d5f25974997662a4b2645d8f95
Author: Karsten Loesing 
Date:   Sat Nov 28 20:35:22 2020 +0100

Add change log entry for #34030 fix.
---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e292f9a..2928937 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
  * Medium changes
   - Clean up descriptors written to the `out/` directory by deleting
 files that are older than seven weeks.
+  - Correctly index files that are moved away and back.
 
 
 # Changes in version 1.16.1 - 2020-08-16

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [collector/master] Fix minor issue with cleaning up directories.

2020-11-28 Thread karsten
commit cd15f3446376847f359040016fb95791dabfce15
Author: Karsten Loesing 
Date:   Sat Nov 28 11:10:30 2020 +0100

Fix minor issue with cleaning up directories.

One of the previously made changes to cleaning up directories was that
empty directories were deleted. This was necessary, because otherwise
there would be a growing number of directories as files get deleted
after reaching an age of seven weeks.

However, this change should not have included deleting the cleaned up
directory itself. In practice, this will not happen. But in tests it's
certainly possible that a directory is empty and then gets deleted.
This leads to all sorts of problems in tests.

The fix is to limit deleting empty directories to subdirectories.
That's what this commit does.
---
 .../org/torproject/metrics/collector/persist/PersistenceUtils.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/persist/PersistenceUtils.java 
b/src/main/java/org/torproject/metrics/collector/persist/PersistenceUtils.java
index e787c39..2b7621d 100644
--- 
a/src/main/java/org/torproject/metrics/collector/persist/PersistenceUtils.java
+++ 
b/src/main/java/org/torproject/metrics/collector/persist/PersistenceUtils.java
@@ -132,7 +132,8 @@ public class PersistenceUtils {
   @Override
   public FileVisitResult postVisitDirectory(Path dir, IOException exc)
   throws IOException {
-if (!Files.list(dir).findFirst().isPresent()) {
+if (!pathToClean.equals(dir)
+&& !Files.list(dir).findFirst().isPresent()) {
   Files.delete(dir);
 }
 return FileVisitResult.CONTINUE;



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [collector/master] Correctly index files that are moved away and back.

2020-11-28 Thread karsten
commit 42a0dd280924cdf1ad5c2be3c6c18536a197c88f
Author: Karsten Loesing 
Date:   Sat Nov 28 11:14:21 2020 +0100

Correctly index files that are moved away and back.

The indexer did not handle a (mostly theoretic) edge case of a file
being moved away and then moved back shortly after. In such a case the
file should not be marked for deletion anymore and it should be
included in the index again. That's what this commit does.

The other minor changes to unit tests are just cosmetic.

Fixes #34030.
---
 .../metrics/collector/indexer/CreateIndexJson.java |  6 ++
 .../collector/indexer/CreateIndexJsonTest.java | 23 +++---
 2 files changed, 26 insertions(+), 3 deletions(-)

diff --git 
a/src/main/java/org/torproject/metrics/collector/indexer/CreateIndexJson.java 
b/src/main/java/org/torproject/metrics/collector/indexer/CreateIndexJson.java
index 6613e9f..3d86947 100644
--- 
a/src/main/java/org/torproject/metrics/collector/indexer/CreateIndexJson.java
+++ 
b/src/main/java/org/torproject/metrics/collector/indexer/CreateIndexJson.java
@@ -426,6 +426,12 @@ public class CreateIndexJson extends CollecTorMain {
   /* We do have index results, but we don't have a link yet, so we're
* going to create a link. */
   linksToCreate.put(linkPath, filePath);
+  if (null != fileNode.markedForDeletion) {
+/* We had already marked the link for deletion, but given that the
+ * original file has returned, we're going to list this file again
+ * and not delete the link in the future. */
+fileNode.markedForDeletion = null;
+  }
 } else {
   String linkLastModified = dateTimeFormatter
   .format(Files.getLastModifiedTime(linkPath).toInstant());
diff --git 
a/src/test/java/org/torproject/metrics/collector/indexer/CreateIndexJsonTest.java
 
b/src/test/java/org/torproject/metrics/collector/indexer/CreateIndexJsonTest.java
index 9c01293..a176d27 100644
--- 
a/src/test/java/org/torproject/metrics/collector/indexer/CreateIndexJsonTest.java
+++ 
b/src/test/java/org/torproject/metrics/collector/indexer/CreateIndexJsonTest.java
@@ -404,8 +404,7 @@ public class CreateIndexJsonTest {
 createFile(recentExitListFilePath, Instant.parse("2016-09-20T13:02:00Z"));
 writeIndexJson(recentExitListIndexJsonString);
 startProcessing(firstExecution);
-startProcessing(secondExecution);
-assertEquals(recentExitListIndexJsonString, readIndexJson());
+assertTrue(this.indexerTasks.isEmpty());
   }
 
   /**
@@ -422,7 +421,6 @@ public class CreateIndexJsonTest {
 deleteFile(recentExitListFilePath);
 startProcessing(secondExecution);
 assertEquals(emptyIndexJsonString, readIndexJson());
-fileExists(recentExitListLinkPath);
 assertTrue(fileExists(recentExitListLinkPath));
 startProcessing(thirdExecution);
 assertFalse(fileExists(recentExitListLinkPath));
@@ -518,5 +516,24 @@ public class CreateIndexJsonTest {
 startProcessing(thirdExecution);
 assertTrue(this.indexerTasks.isEmpty());
   }
+
+  /**
+   * Test whether a file that was previously contained in the index and deleted
+   * or moved away and that is later recreated or moved back to its original
+   * location is included in the index again and still has a corresponding link
+   * three hours later.
+   */
+  @Test
+  public void testMoveBackFile() {
+writeIndexJson(recentExitListIndexJsonString);
+startProcessing(firstExecution);
+createFile(recentExitListFilePath, Instant.parse("2016-09-20T13:02:00Z"));
+startProcessing(secondExecution);
+assertTrue(this.indexerTasks.isEmpty());
+assertTrue(Files.exists(recentExitListLinkPath));
+assertEquals(recentExitListIndexJsonString, readIndexJson());
+startProcessing(thirdExecution);
+assertTrue(Files.exists(recentExitListLinkPath));
+  }
 }
 

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [collector/master] Bump version to 1.16.1-dev.

2020-11-27 Thread karsten
commit 91d10a09608df31f3ef50f9ec3faa156d8bc2713
Author: Karsten Loesing 
Date:   Wed Nov 25 16:08:39 2020 +0100

Bump version to 1.16.1-dev.
---
 build.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.xml b/build.xml
index e6e727a..5dcb29d 100644
--- a/build.xml
+++ b/build.xml
@@ -9,7 +9,7 @@
 
   
   
-  
+  
   
   
   



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [collector/master] Delete files in out/ that are older than 7 weeks.

2020-11-27 Thread karsten
commit 66ddc4d7d996ad2877aea44ea03982f14f069545
Author: Karsten Loesing 
Date:   Wed Nov 25 16:09:14 2020 +0100

Delete files in out/ that are older than 7 weeks.

Fixes #21219.
---
 CHANGELOG.md   |  7 +++
 .../bridgedb/BridgedbMetricsProcessor.java | 38 --
 .../bridgedescs/SanitizedBridgesWriter.java| 37 +
 .../BridgePoolAssignmentsProcessor.java| 33 
 .../collector/exitlists/ExitListDownloader.java| 27 --
 .../collector/onionperf/OnionPerfDownloader.java   | 37 ++---
 .../collector/persist/PersistenceUtils.java| 50 +++---
 .../collector/relaydescs/ArchiveWriter.java| 61 +-
 .../snowflake/SnowflakeStatsDownloader.java| 33 +---
 .../metrics/collector/sync/SyncPersistence.java|  7 +--
 .../collector/webstats/SanitizeWeblogs.java| 12 +++--
 .../collector/persist/PersistUtilsTest.java| 32 
 12 files changed, 182 insertions(+), 192 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ff2e9e7..e292f9a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+# Changes in version 1.??.? - 2020-1?-??
+
+ * Medium changes
+  - Clean up descriptors written to the `out/` directory by deleting
+files that are older than seven weeks.
+
+
 # Changes in version 1.16.1 - 2020-08-16
 
  * Medium changes
diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedb/BridgedbMetricsProcessor.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedb/BridgedbMetricsProcessor.java
index 0073ee3..d05aa9c 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedb/BridgedbMetricsProcessor.java
+++ 
b/src/main/java/org/torproject/metrics/collector/bridgedb/BridgedbMetricsProcessor.java
@@ -12,6 +12,7 @@ import 
org.torproject.metrics.collector.conf.ConfigurationException;
 import org.torproject.metrics.collector.conf.Key;
 import org.torproject.metrics.collector.cron.CollecTorMain;
 import org.torproject.metrics.collector.persist.BridgedbMetricsPersistence;
+import org.torproject.metrics.collector.persist.PersistenceUtils;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -23,9 +24,7 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
-import java.util.Arrays;
 import java.util.SortedSet;
-import java.util.Stack;
 import java.util.TreeSet;
 
 public class BridgedbMetricsProcessor extends CollecTorMain {
@@ -127,10 +126,10 @@ public class BridgedbMetricsProcessor extends 
CollecTorMain {
 descriptor.getClass(), descriptor.getDescriptorFile());
   }
 }
-logger.info("Cleaning up directory {} containing recent files.",
-this.recentPathName);
+logger.info("Cleaning up directories {} and {}.",
+this.recentPathName, this.outputPathName);
 this.writeProcessedFiles(this.parsedBridgedbMetricsFile, processedFiles);
-this.cleanUpRsyncDirectory();
+this.cleanUpDirectories();
 logger.info("Finished processing BridgeDB statistics file(s).");
   }
 
@@ -175,28 +174,13 @@ public class BridgedbMetricsProcessor extends 
CollecTorMain {
   }
 
   /**
-   * Delete all files from the rsync directory that have not been modified in
-   * the last three days.
+   * Delete all files from the rsync (out) directory that have not been 
modified
+   * in the last three days (seven weeks).
*/
-  public void cleanUpRsyncDirectory() {
-Instant cutOff = Instant.now().minus(3L, ChronoUnit.DAYS);
-Stack allFiles = new Stack<>();
-allFiles.add(new File(this.recentPathName));
-while (!allFiles.isEmpty()) {
-  File file = allFiles.pop();
-  if (file.isDirectory()) {
-File[] filesInDirectory = file.listFiles();
-if (null != filesInDirectory) {
-  allFiles.addAll(Arrays.asList(filesInDirectory));
-}
-  } else if (Instant.ofEpochMilli(file.lastModified()).isBefore(cutOff)) {
-try {
-  Files.deleteIfExists(file.toPath());
-} catch (IOException e) {
-  logger.warn("Unable to delete file {} that is apparently older than "
-  + "three days.", file, e);
-}
-  }
-}
+  private void cleanUpDirectories() {
+PersistenceUtils.cleanDirectory(Paths.get(this.recentPathName),
+Instant.now().minus(3, ChronoUnit.DAYS).toEpochMilli());
+PersistenceUtils.cleanDirectory(Paths.get(this.outputPathName),
+Instant.now().minus(49, ChronoUnit.DAYS).toEpochMilli());
   }
 }
diff --git 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
 
b/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.java
index b8e7f2d..62288ad 100644
--- 
a/src/main/java/org/torproject/metrics/collector/bridgedescs/SanitizedBridgesWriter.ja

[tor-commits] [metrics-web/master] Update from Metrics Timeline, and add links.

2020-09-21 Thread karsten
commit e54b73882132911fa9ec0809bba0823fb562d6a4
Author: Karsten Loesing 
Date:   Tue Sep 15 09:38:32 2020 +0200

Update from Metrics Timeline, and add links.
---
 src/main/resources/web/json/news.json | 19 ---
 src/main/resources/web/jsps/graph.jsp |  4 ++--
 src/main/resources/web/jsps/news.jsp  |  2 +-
 3 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/src/main/resources/web/json/news.json 
b/src/main/resources/web/json/news.json
index 43c28e8..901b00c 100644
--- a/src/main/resources/web/json/news.json
+++ b/src/main/resources/web/json/news.json
@@ -5,7 +5,7 @@
   "description" : "Shutdown of default obfs4 bridge frosty.",
   "links" : [ {
 "label" : "ticket",
-"target" : 
"https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/issues/40066;
+"target" : 
"https://bugs.torproject.org/tpo/applications/tor-browser-build/40066;
   } ]
 }, {
   "start" : "2020-08-13",
@@ -14,7 +14,7 @@
   "description" : "Deployed version 0.4.1 of the Snowflake WebExtension, with 
NAT type detection in the browser.",
   "links" : [ {
 "label" : "comment",
-"target" : 
"https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake-webext/-/issues/13#note_2705805;
+"target" : 
"https://bugs.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake-webext/13#note_2705805;
   } ]
 }, {
   "start" : "2020-07-06",
@@ -23,7 +23,7 @@
   "description" : "Deployed NAT-aware matching at the Snowflake broker.",
   "links" : [ {
 "label" : "comment",
-"target" : 
"https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/issues/34129#note_2685268;
+"target" : 
"https://bugs.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/34129#note_2685268;
   } ]
 }, {
   "start" : "2020-06-30",
@@ -3540,6 +3540,19 @@
 "label" : "commit",
 "target" : 
"https://gitweb.torproject.org/tor.git/commit/?id=1496056c121d02193433d98173213e52eb3e90a7;
   } ]
+}, {
+  "start" : "2015-12-31",
+  "end" : "2020-06-04",
+  "places" : [ "mx" ],
+  "short_description" : "The Telmex ISP in Mexico blocks seven directory 
authorities.",
+  "description" : "The Telmex ISP in Mexico blocks seven directory 
authorities.",
+  "links" : [ {
+"label" : "mailing list post",
+"target" : 
"https://lists.torproject.org/pipermail/tor-relays/2016-January/008491.html;
+  }, {
+"label" : "article",
+"target" : 
"https://globalvoices.org/2020/09/08/we-made-the-largest-mexican-telecommunications-operator-stop-blocking-secure-internet/;
+  } ]
 }, {
   "start" : "2015-12-25",
   "protocols" : [ "meek" ],
diff --git a/src/main/resources/web/jsps/graph.jsp 
b/src/main/resources/web/jsps/graph.jsp
index 94aa784..8d601e5 100644
--- a/src/main/resources/web/jsps/graph.jsp
+++ b/src/main/resources/web/jsps/graph.jsp
@@ -160,8 +160,8 @@
   
 
   Related events
-  The following events have been manually collected on
-  https://trac.torproject.org/projects/tor/wiki/doc/MetricsTimeline; 
target="_blank">this wiki page
+  The following events have been manually collected in the
+  https://gitlab.torproject.org/tpo/metrics/timeline; 
target="_blank">metrics-timeline Git repository
   and might be related to the displayed graph.
   
   
diff --git a/src/main/resources/web/jsps/news.jsp 
b/src/main/resources/web/jsps/news.jsp
index abde1e0..d78cde9 100644
--- a/src/main/resources/web/jsps/news.jsp
+++ b/src/main/resources/web/jsps/news.jsp
@@ -19,7 +19,7 @@
 
   
   News #
-  You're a journalist or more generally a person who wants to know 
what's going on in the Tor network?  We're collecting unusual events in the Tor 
network together with any insights we have into what we think has happened.
+  You're a journalist or more generally a person who wants to know 
what's going on in the Tor network?  We're collecting unusual events in the Tor 
network together with any insights we have into what we think has happened in 
the https://gitlab.torproject.org/tpo/metrics/timeline; 
target="_blank">metrics-timeline Git repository.
 
 
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [metrics-web/master] Update OnionPerf link.

2020-09-21 Thread karsten
commit c91b83230639d6779b06872a600e361b3cb388d1
Author: Karsten Loesing 
Date:   Mon Sep 21 15:58:05 2020 +0200

Update OnionPerf link.
---
 src/main/resources/web/jsps/sources.jsp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/main/resources/web/jsps/sources.jsp 
b/src/main/resources/web/jsps/sources.jsp
index 1f2930c..eb0ebc6 100644
--- a/src/main/resources/web/jsps/sources.jsp
+++ b/src/main/resources/web/jsps/sources.jsp
@@ -34,8 +34,7 @@
   
 https://gitweb.torproject.org/user/phw/exitmap.git; 
target="_blank">Exitmap is a fast and extensible scanner for Tor exit 
relays.
 https://www.torproject.org/projects/tordnsel.html.en; 
target="_blank">TorDNSEL publishes lists of IP addresses of multi-homed Tor 
exits.
-https://gitweb.torproject.org/torperf.git; 
target="_blank">Torperf measures Tor performance with a set of utilities 
and Python scripts.
-https://github.com/robgjansen/onionperf; 
target="_blank">OnionPerf measures the performance of onion services.
+https://gitlab.torproject.org/tpo/metrics/onionperf; 
target="_blank">OnionPerf measures Tor performance with a set of utilities 
and Python scripts.
 https://ooni.torproject.org/; target="_blank">OONI 
detects censorship, surveillance, and traffic manipulation on the internet.
 https://gitweb.torproject.org/user/phw/sybilhunter.git/; 
target="_blank">Sybilhunter attempts to detect Sybil attacks on the Tor 
network.
   

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Append to existing "filters" field.

2020-09-18 Thread karsten
commit c41c0b74c47442dc6f6d23f49a012729f71aee49
Author: Karsten Loesing 
Date:   Wed Sep 16 12:01:07 2020 +0200

Append to existing "filters" field.

Before this change we would have replaced an existing "filters" field
with the filters applied in this run. But if the user runs `onionperf
filter` more than once to apply different filters, we'll want to list
all filters in the "filters" field.

This change also removes a couple of `self.`s, in particular
`self.analysis` and `self.filters`, because one Filtering object can
apply filters to more than one analysis.
---
 onionperf/filtering.py | 20 +---
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index e8ea164..15d9c19 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -7,7 +7,6 @@
 
 import re
 from onionperf.analysis import OPAnalysis
-from collections import defaultdict
 
 class Filtering(object):
 
@@ -15,7 +14,6 @@ class Filtering(object):
 self.fingerprints_to_include = None
 self.fingerprints_to_exclude = None
 self.fingerprint_pattern = re.compile("\$?([0-9a-fA-F]{40})")
-self.filters = defaultdict(list)
 
 def include_fingerprints(self, path):
 self.fingerprints_to_include = []
@@ -40,11 +38,12 @@ class Filtering(object):
 def filter_tor_circuits(self, analysis):
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
 return
-self.filters["tor/circuits"] = []
+filters = analysis.json_db.setdefault("filters", {})
+tor_circuits_filters = filters.setdefault("tor/circuits", [])
 if self.fingerprints_to_include:
-   self.filters["tor/circuits"].append({"name": 
"include_fingerprints", "filepath": self.fingerprints_to_include_path })
+   tor_circuits_filters.append({"name": "include_fingerprints", 
"filepath": self.fingerprints_to_include_path })
 if self.fingerprints_to_exclude:
-   self.filters["tor/circuits"].append({"name": 
"exclude_fingerprints", "filepath": self.fingerprints_to_exclude_path })
+   tor_circuits_filters.append({"name": "exclude_fingerprints", 
"filepath": self.fingerprints_to_exclude_path })
 for source in analysis.get_nodes():
 tor_circuits = analysis.get_tor_circuits(source)
 filtered_circuit_ids = []
@@ -68,10 +67,9 @@ class Filtering(object):
 tor_circuits[circuit_id] = 
dict(sorted(tor_circuit.items()))
 
 def apply_filters(self, input_path, output_dir, output_file):
-self.analysis = OPAnalysis.load(filename=input_path)
-self.filter_tor_circuits(self.analysis)
-self.analysis.json_db["filters"] = self.filters
-self.analysis.json_db["version"] = '4.0'
-self.analysis.json_db = dict(sorted(self.analysis.json_db.items()))
-self.analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
+analysis = OPAnalysis.load(filename=input_path)
+self.filter_tor_circuits(analysis)
+analysis.json_db["version"] = '4.0'
+analysis.json_db = dict(sorted(analysis.json_db.items()))
+analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Only try to load analysis files in an input directory

2020-09-18 Thread karsten
commit d21d41e4d504b48ac5617530830da320a89a9eed
Author: Ana Custura 
Date:   Sat Aug 29 15:10:32 2020 +0100

Only try to load analysis files in an input directory
---
 onionperf/onionperf | 11 ++-
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/onionperf/onionperf b/onionperf/onionperf
index 7c16aea..e3f49c8 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -458,11 +458,12 @@ def filter(args):
 output_dir, output_file = os.path.split(output_path)
 filtering.apply_filters(input_path=input_path, output_dir=output_dir, 
output_file=output_file)
 else:
-for dirpath, dirnames, filenames in os.walk(input_path):
-for filename in filenames:
-input_file = os.path.join(dirpath, filename)
-output_dir = os.path.join(output_path, 
os.path.relpath(dirpath, input_path))
-filtering.apply_filters(input_path=input_file, 
output_dir=output_dir, output_file=filename)
+from onionperf import reprocessing
+analyses = reprocessing.collect_logs(input_path, 
'*onionperf.analysis.*')
+for analysis in analyses:
+full_output_path = os.path.join(output_path, 
os.path.relpath(analysis, input_path))
+output_dir, output_file = os.path.split(full_output_path)
+filtering.apply_filters(input_path=analysis, 
output_dir=output_dir, output_file=output_file)
 
 def visualize(args):
 from onionperf.visualization import TGenVisualization



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Change filter mode to filter Tor circuits.

2020-09-18 Thread karsten
commit 9d0c80561b84905e72b41758dfcb7b712f5f407f
Author: Karsten Loesing 
Date:   Mon Aug 31 11:30:53 2020 +0200

Change filter mode to filter Tor circuits.

This new filter mode removes Tor circuits that don't match the
provided fingerprints and leaves TGen transfers/streams untouched. At
the same time the visualize mode only includes TGen transfers/streams
with an existing mapping between TGen transfers/streams and Tor
streams/circuits.

This patch changes the default behavior of the visualize mode. The
original behavior of visualizing TGen transfers/streams *without* an
existing mapping to Tor streams/circuits can be selected with the
--outer-join switch, even though that's rather an edge use case.

Another minor change is that the filtered analysis files is not
written with sort_keys=True anymore, which would have produced a newly
sorted file with keys in alphabetic order rather than the original
insert order. The result is an actually useful diff.
---
 onionperf/analysis.py  | 13 +---
 onionperf/filtering.py | 49 --
 onionperf/onionperf| 24 +--
 onionperf/visualization.py | 33 ---
 4 files changed, 53 insertions(+), 66 deletions(-)

diff --git a/onionperf/analysis.py b/onionperf/analysis.py
index b2f483f..49c109c 100644
--- a/onionperf/analysis.py
+++ b/onionperf/analysis.py
@@ -62,13 +62,7 @@ class OPAnalysis(Analysis):
 self.json_db['data'][self.nickname]["tgen"].pop("stream_summary")
 self.did_analysis = True
 
-def set_tgen_transfers(self, node, tgen_transfers):
-self.json_db['data'][node]['tgen']['transfers'] = tgen_transfers
-
-def set_tgen_streams(self, node, tgen_streams):
-self.json_db['data'][node]['tgen']['streams'] = tgen_streams
-
-def save(self, filename=None, output_prefix=os.getcwd(), do_compress=True, 
date_prefix=None):
+def save(self, filename=None, output_prefix=os.getcwd(), do_compress=True, 
date_prefix=None, sort_keys=True):
 if filename is None:
 base_filename = "onionperf.analysis.json.xz"
 if date_prefix is not None:
@@ -85,7 +79,7 @@ class OPAnalysis(Analysis):
 logging.info("saving analysis results to {0}".format(filepath))
 
 outf = util.FileWritable(filepath, do_compress=do_compress)
-json.dump(self.json_db, outf, sort_keys=True, separators=(',', ': '), 
indent=2)
+json.dump(self.json_db, outf, sort_keys=sort_keys, separators=(',', ': 
'), indent=2)
 outf.close()
 
 logging.info("done!")
@@ -109,6 +103,9 @@ class OPAnalysis(Analysis):
 except:
 return None
 
+def set_tor_circuits(self, node, tor_circuits):
+self.json_db['data'][node]['tor']['circuits'] = tor_circuits
+
 def get_tor_streams(self, node):
 try:
 return self.json_db['data'][node]['tor']['streams']
diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 9e7b34f..1b614d6 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -38,41 +38,11 @@ class Filtering(object):
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
 return
 for source in self.analysis.get_nodes():
-tor_streams_by_source_port = {}
-tor_streams = self.analysis.get_tor_streams(source)
-for tor_stream in tor_streams.values():
-if "source" in tor_stream and ":" in tor_stream["source"]:
-source_port = tor_stream["source"].split(":")[1]
-tor_streams_by_source_port.setdefault(source_port, 
[]).append(tor_stream)
 tor_circuits = self.analysis.get_tor_circuits(source)
-tgen_streams = self.analysis.get_tgen_streams(source)
-tgen_transfers = self.analysis.get_tgen_transfers(source)
-retained_tgen_streams = {}
-retained_tgen_transfers = {}
-while tgen_streams or tgen_transfers:
-stream_id = None
-transfer_id = None
-source_port = None
-unix_ts_end = None
+filtered_circuit_ids = []
+for circuit_id, tor_circuit in tor_circuits.items():
 keep = False
-if tgen_streams:
-stream_id, stream_data = tgen_streams.popitem()
-if "local" in stream_data["transport_info"] and 
len(stream_data["transport_info"]["local"].split(":")) > 2:
-source_port = 
stream_data["transport_info"]["local"].split(":")[2]
-if "unix_ts_end" in stream_data:
-

[tor-commits] [onionperf/master] Move filters and filter metadata to analysis files

2020-09-18 Thread karsten
commit 95b749a8fc690825c0a828b8473c58faea7ad912
Author: Ana Custura 
Date:   Thu Sep 10 01:51:54 2020 +0100

Move filters and filter metadata to analysis files
---
 onionperf/filtering.py | 25 ++---
 onionperf/onionperf|  9 +
 onionperf/visualization.py | 14 +-
 3 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 1b614d6..c008c03 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -7,6 +7,7 @@
 
 import re
 from onionperf.analysis import OPAnalysis
+from collections import defaultdict
 
 class Filtering(object):
 
@@ -14,9 +15,11 @@ class Filtering(object):
 self.fingerprints_to_include = None
 self.fingerprints_to_exclude = None
 self.fingerprint_pattern = re.compile("\$?([0-9a-fA-F]{40})")
+self.filters = defaultdict(list)
 
 def include_fingerprints(self, path):
 self.fingerprints_to_include = []
+self.fingerprints_to_include_path = path
 with open(path, 'rt') as f:
 for line in f:
 fingerprint_match = self.fingerprint_pattern.match(line)
@@ -26,6 +29,7 @@ class Filtering(object):
 
 def exclude_fingerprints(self, path):
 self.fingerprints_to_exclude = []
+self.fingerprints_to_exclude_path = path
 with open(path, 'rt') as f:
 for line in f:
 fingerprint_match = self.fingerprint_pattern.match(line)
@@ -33,12 +37,16 @@ class Filtering(object):
 fingerprint = fingerprint_match.group(1).upper()
 self.fingerprints_to_exclude.append(fingerprint)
 
-def apply_filters(self, input_path, output_dir, output_file):
-self.analysis = OPAnalysis.load(filename=input_path)
+def filter_tor_circuits(self, analysis):
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
 return
-for source in self.analysis.get_nodes():
-tor_circuits = self.analysis.get_tor_circuits(source)
+self.filters["tor/circuits"] = []
+if self.fingerprints_to_include:
+   self.filters["tor/circuits"].append({"name": 
"include_fingerprints", "filepath": self.fingerprints_to_include_path })
+if self.fingerprints_to_exclude:
+   self.filters["tor/circuits"].append({"name": 
"exclude_fingerprints", "filepath": self.fingerprints_to_exclude_path })
+for source in analysis.get_nodes():
+tor_circuits = analysis.get_tor_circuits(source)
 filtered_circuit_ids = []
 for circuit_id, tor_circuit in tor_circuits.items():
 keep = False
@@ -56,8 +64,11 @@ class Filtering(object):
 keep = False
 break
 if not keep:
-filtered_circuit_ids.append(circuit_id)
-for circuit_id in filtered_circuit_ids:
-del(tor_circuits[circuit_id])
+tor_circuits[circuit_id]["filtered"] = True
+
+def apply_filters(self, input_path, output_dir, output_file):
+self.analysis = OPAnalysis.load(filename=input_path)
+self.filter_tor_circuits(self.analysis)
+self.analysis.json_db["filters"] = self.filters
 self.analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
 
diff --git a/onionperf/onionperf b/onionperf/onionperf
index 1efa8cb..108af4e 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -342,13 +342,6 @@ files generated by this script will be written""",
 required="True",
 action=PathStringArgsAction, dest="datasets")
 
-visualize_parser.add_argument('--outer-join',
-help="""Include measurements without an existing mapping between TGen
-transfers/streams and Tor streams/circuits, which is the
-equivalent of an outer join in the database sense""",
-action="store_true", dest="outer_join",
-default=False)
-
 visualize_parser.add_argument('-p', '--prefix',
 help="a STRING filename prefix for graphs we generate",
 metavar="STRING", type=str,
@@ -489,7 +482,7 @@ def visualize(args):
 if analysis is not None:
analyses.append(analysis)
 tgen_viz.add_dataset(analyses, label)
-tgen_viz.plot_all(args.prefix, outer_join=args.outer_join)
+tgen_viz.plot_all(args.prefix)
 
 def type_nonnegative_integer(value):
 i = int(value)
diff --git a/onionperf/visualization.py b/onionperf/visualization.py
index 0f69879..f5bc03f 100644
--- a/onionperf/visualization.py
+++ b/onionperf/visualization.py
@@ -31,11 +31,11 @@ class Visualization(object, metaclass=ABCMeta):
 
 class TGenVisualization(Visualization):
 
-def plot_all(self, output_prefix, outer_join=False):
+def plot_all(self, output_prefix):
 if len(self.datasets) > 0:
 

[tor-commits] [onionperf/master] Update README and CHANGELOG

2020-09-18 Thread karsten
commit 1b638e34cb0476148dcf93b43b58b54a30c27428
Author: Ana Custura 
Date:   Thu Sep 10 14:46:10 2020 +0100

Update README and CHANGELOG
---
 CHANGELOG.md |  5 +
 README.md| 10 +++---
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ebd43e1..a7d5005 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,11 @@
  - Add a new `onionperf filter` mode that takes an OnionPerf analysis
results file or directory as input, applies filters, and produces
new OnionPerf analysis results file(s) as output.
+ - Bump the analysis version number to 4.0 for new analyses and analyses
+   produced by the `onionperf filter` mode
+ - Analyses produced by the `onionperf filter` mode have additional filter
+   metadata defined in a new 'filters' field, and an optional 'filtered\_out'
+   field per tor circuit
 
 # Changes in version 0.6 - 2020-08-08
 
diff --git a/README.md b/README.md
index 224972c..13a9eee 100644
--- a/README.md
+++ b/README.md
@@ -254,10 +254,12 @@ onionperf analyze --help
 ```
 ### Filtering measurement results
 
-OnionPerf measurement results can be filtered based on Tor relay fingerprints.
-The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs, and outputs new analysis files containing only the 
`tgen` results obtained over a Tor circuit path which includes or excludes 
fingerprints in the input list.
+The `filter` subcommand is typically used in combination with the `visualize` 
subcommand. The workflow is to filter out any Tor streams/circuits that are not 
desired then visualize only those measurements with an existing mapping between 
TGen transfers/streams and Tor streams/circuits.
 
-Where excluding fingerprints, if ANY relay fingerprint is matched, the 
measurement is discarded. Where including fingerprints, ALL relay fingerprints 
in a path must match for the measurement to be retained.
+Currently, OnionPerf measurement results can be filtered based on Tor relay 
fingerprints, although support for filtering TGen based on transfers/streams 
may be added in the future.
+
+The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs, and outputs new analysis files which include, 
unchanged, the Tor results obtained over a Tor circuit path which includes or 
excludes fingerprints in the input list. All other Tor results are also 
included in the file, but are marked as 'filtered\_out'.
+Filter metadata detailing the filter type and path to the input list used is 
also included in the analysis file.
 
 For example, the analysis file produced above can be filtered with the 
following command, which retains measurements based on fingerprints contained 
in the file 'fingerprints.txt':
 
@@ -290,6 +292,8 @@ As a result, two files are written to the current working 
directory:
 - `onionperf.viz.$datetime.csv` contains visualized data in a CSV file format; 
and
 - `onionperf.viz.$datetime.pdf` contains visualizations in a PDF file format.
 
+For analysis files containing tor circuit filters, only measurements with an 
existing mapping between TGen transfers/streams Tor streams/circuits which have 
not been marked as 'filtered\_out' are visualized.
+
 Similar to the other modes, OnionPerf's `visualize` mode has command-line 
parameters for customizing the visualization step:
 
 ```shell



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Order analysis and filter keys, authored by Karsten

2020-09-18 Thread karsten
commit 7b0c91f1983ab21f39f37b83d3a58db5a4d11a8f
Author: Ana Custura 
Date:   Thu Sep 10 12:37:00 2020 +0100

Order analysis and filter keys, authored by Karsten
---
 onionperf/filtering.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index c008c03..7ef6168 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -65,10 +65,12 @@ class Filtering(object):
 break
 if not keep:
 tor_circuits[circuit_id]["filtered"] = True
+tor_circuits[circuit_id] = 
dict(sorted(tor_circuit.items()))
 
 def apply_filters(self, input_path, output_dir, output_file):
 self.analysis = OPAnalysis.load(filename=input_path)
 self.filter_tor_circuits(self.analysis)
 self.analysis.json_db["filters"] = self.filters
+self.analysis.json_db = dict(sorted(self.analysis.json_db.items()))
 self.analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Bump analysis version to 4.0

2020-09-18 Thread karsten
commit d0660c1847b46863783b7006efdac4df8103f123
Author: Ana Custura 
Date:   Thu Sep 10 12:54:41 2020 +0100

Bump analysis version to 4.0
---
 onionperf/analysis.py  | 4 ++--
 onionperf/filtering.py | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/onionperf/analysis.py b/onionperf/analysis.py
index 49c109c..907d451 100644
--- a/onionperf/analysis.py
+++ b/onionperf/analysis.py
@@ -24,7 +24,7 @@ class OPAnalysis(Analysis):
 
 def __init__(self, nickname=None, ip_address=None):
 super().__init__(nickname, ip_address)
-self.json_db = {'type': 'onionperf', 'version': '3.0', 'data': {}}
+self.json_db = {'type': 'onionperf', 'version': '4.0', 'data': {}}
 self.torctl_filepaths = []
 
 def add_torctl_file(self, filepath):
@@ -133,7 +133,7 @@ class OPAnalysis(Analysis):
 if 'type' not in db or 'version' not in db:
 logging.warning("'type' or 'version' not present in database")
 return None
-elif db['type'] != 'onionperf' or str(db['version']) >= '4.':
+elif db['type'] != 'onionperf' or str(db['version']) >= '5.':
 logging.warning("type or version not supported (type={0}, 
version={1})".format(db['type'], db['version']))
 return None
 else:
diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 0ba9ebc..e8ea164 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -71,6 +71,7 @@ class Filtering(object):
 self.analysis = OPAnalysis.load(filename=input_path)
 self.filter_tor_circuits(self.analysis)
 self.analysis.json_db["filters"] = self.filters
+self.analysis.json_db["version"] = '4.0'
 self.analysis.json_db = dict(sorted(self.analysis.json_db.items()))
 self.analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Merge branch 'acute-task-33260-4' into develop

2020-09-18 Thread karsten
commit 76517ac669f3e7575965662507ab970b41395324
Merge: c8275b2 f063b63
Author: Karsten Loesing 
Date:   Wed Sep 16 12:28:19 2020 +0200

Merge branch 'acute-task-33260-4' into develop

 CHANGELOG.md   |   9 +
 README.md  |  23 ++
 onionperf/analysis.py  |  18 +-
 onionperf/filtering.py |  75 ++
 onionperf/onionperf|  73 ++
 onionperf/visualization.py |  31 ++-
 schema/onionperf-4.0.json  | 577 +
 7 files changed, 793 insertions(+), 13 deletions(-)

diff --cc CHANGELOG.md
index 80fb14d,13fb202..2c1f5ca
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@@ -1,13 -1,12 +1,22 @@@
+ # Changes in version 0.8 - 2020-09-16
+ 
+  - Add a new `onionperf filter` mode that takes an OnionPerf analysis
+results file or directory as input, applies filters, and produces
+new OnionPerf analysis results file(s) as output. Bump the analysis
+version number to 4.0 to include additional filter metadata defined
+in a 'filters' field and an optional 'filtered\_out' field per Tor
+circuit. Implements #33260.
+ 
 +# Changes in version 0.7 - 2020-09-01
 +
 + - Add `onionperf measure --drop-guards` parameter to use and drop
 +   guards and circuit build timeouts every given number of hours, if
 +   supported by the Tor version. Implements #33399.
 + - Remove the `onionperf measure --oneshot` switch and replace it with
 +   new switches `--tgen-pause-initial`, `--tgen-pause-between`,
 +   `--tgen-transfer-size`, and `--tgen-num-transfers ` to further
 +   configure the generated TGen model. Implemets #33432.
 +
  # Changes in version 0.6 - 2020-08-08
  
   - Update to TGen 1.0.0, use TGenTools for parsing TGen log files, and



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Add JSON schema for analysis version 4.0.

2020-09-18 Thread karsten
commit a4a4dcc97d07b2668fbab593165119bd52f694ed
Author: Karsten Loesing 
Date:   Wed Sep 16 11:19:58 2020 +0200

Add JSON schema for analysis version 4.0.
---
 schema/onionperf-4.0.json | 577 ++
 1 file changed, 577 insertions(+)

diff --git a/schema/onionperf-4.0.json b/schema/onionperf-4.0.json
new file mode 100644
index 000..3c4dba3
--- /dev/null
+++ b/schema/onionperf-4.0.json
@@ -0,0 +1,577 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema;,
+  "$id": 
"https://gitlab.torproject.org/tpo/metrics/onionperf/-/raw/master/schema/onionperf-4.0.json;,
+  "type": "object",
+  "title": "OnionPerf analysis JSON file format 4.0",
+  "required": [
+"data",
+"type",
+"version"
+  ],
+  "properties": {
+"data": {
+  "type": "object",
+  "title": "Measurement data by source name",
+  "propertyNames": {
+"pattern": "^[A-Za-z0-9-]+$"
+  },
+  "additionalProperties": {
+"type": "object",
+"title": "Measurement data from a single source",
+"required": [
+  "measurement_ip",
+  "tgen",
+  "tor"
+],
+"properties": {
+  "measurement_ip": {
+"type": "string",
+"title": "Public IP address of the measuring host."
+  },
+  "tgen": {
+"type": "object",
+"title": "Measurement data obtained from client-side TGen logs",
+"required": [
+  "streams"
+],
+"properties": {
+  "streams": {
+"type": "object",
+"title": "Measurement data, by TGen stream identifier",
+"additionalProperties": {
+  "type": "object",
+  "title": "Information on a single measurement, obtained from 
a single [stream-success] or [stream-error] log message (except for 
elapsed_seconds)",
+  "required": [
+"byte_info",
+"is_complete",
+"is_error",
+"is_success",
+"stream_id",
+"stream_info",
+"time_info",
+"transport_info",
+"unix_ts_end",
+"unix_ts_start"
+  ],
+  "properties": {
+"byte_info": {
+  "type": "object",
+  "title": "Information on sent and received bytes",
+  "required": [
+"payload-bytes-recv",
+"payload-bytes-send",
+"payload-progress-recv",
+"payload-progress-send",
+"total-bytes-recv",
+"total-bytes-send"
+  ],
+  "properties": {
+"payload-bytes-recv": {
+  "type": "string",
+  "pattern": "^[0-9]+$",
+  "title": "Number of payload bytes received"
+},
+"payload-bytes-send": {
+  "type": "string",
+  "pattern": "^[0-9]+$",
+  "title": "Number of payload bytes sent"
+},
+"payload-progress-recv": {
+  "type": "string",
+  "pattern": "^[0-9]+\\.[0-9]+%$",
+  "title": "Progress of receiving payload in percent"
+},
+"payload-progress-send": {
+  "type": "string",
+  "pattern": "^[0-9]+\\.[0-9]+%$",
+  "title": "Progress of sending payload in percent"
+},
+   

[tor-commits] [onionperf/master] Add filter section to README.md

2020-09-18 Thread karsten
commit 52506522bc6dc679318e2af801f7c3a94b6947d0
Author: Ana Custura 
Date:   Sat Aug 29 17:01:13 2020 +0100

Add filter section to README.md
---
 README.md | 23 +++
 1 file changed, 23 insertions(+)

diff --git a/README.md b/README.md
index ad53a9e..224972c 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@
 + [Troubleshooting](#troubleshooting)
   * [Analysis](#analysis)
 + [Analyzing measurement results](#analyzing-measurement-results)
++ [Filtering measurement results](#filtering-measurement-results)
 + [Visualizing measurement results](#visualizing-measurement-results)
 + [Interpreting the PDF output format](#interpreting-the-pdf-output-format)
 + [Interpreting the CSV output format](#interpreting-the-csv-output-format)
@@ -251,6 +252,28 @@ OnionPerf's `analyze` mode has several command-line 
parameters for customizing t
 ```shell
 onionperf analyze --help
 ```
+### Filtering measurement results
+
+OnionPerf measurement results can be filtered based on Tor relay fingerprints.
+The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs, and outputs new analysis files containing only the 
`tgen` results obtained over a Tor circuit path which includes or excludes 
fingerprints in the input list.
+
+Where excluding fingerprints, if ANY relay fingerprint is matched, the 
measurement is discarded. Where including fingerprints, ALL relay fingerprints 
in a path must match for the measurement to be retained.
+
+For example, the analysis file produced above can be filtered with the 
following command, which retains measurements based on fingerprints contained 
in the file 'fingerprints.txt':
+
+```shell
+onionperf filter -i onionperf.analysis.json.xz -o 
filtered.onionperf.analysis.json.xz --include-fingerprints fingerprints.txt
+```
+
+The output analysis file is written to the path specified with `-o`. If 
processing a directory of analysis files, its structure and filenames are 
preserved under the path specified with '-o'.
+Note that while the subcommand filters `tgen` measurements, it leaves `tgen` 
and `tor` summaries in the original analysis file unchanged.
+
+OnionPerf's `filter` command usage can be inspected with:
+
+```shell
+onionperf filter --help
+```
+
 
 ### Visualizing measurement results
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Bump version to 0.8.

2020-09-18 Thread karsten
commit 118d14990ce4821232ed6751c6a3d2b05e872dbe
Author: Karsten Loesing 
Date:   Wed Sep 16 12:29:43 2020 +0200

Bump version to 0.8.
---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index d72b27a..b7b00d0 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ with open('requirements.txt') as f:
 install_requires = f.readlines()
 
 setup(name='OnionPerf',
-  version='0.7',
+  version='0.8',
   description='A utility to monitor, measure, analyze, and visualize the 
performance of Tor and Onion Services',
   author='Rob Jansen',
   url='https://github.com/robgjansen/onionperf/',

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Clarify filter terminology

2020-09-18 Thread karsten
commit a466221c5b947bb894a36fd3e5472397f1b0b03e
Author: Ana Custura 
Date:   Thu Sep 10 12:41:46 2020 +0100

Clarify filter terminology
---
 onionperf/filtering.py | 2 +-
 onionperf/visualization.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 7ef6168..0ba9ebc 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -64,7 +64,7 @@ class Filtering(object):
 keep = False
 break
 if not keep:
-tor_circuits[circuit_id]["filtered"] = True
+tor_circuits[circuit_id]["filtered_out"] = True
 tor_circuits[circuit_id] = 
dict(sorted(tor_circuit.items()))
 
 def apply_filters(self, input_path, output_dir, output_file):
diff --git a/onionperf/visualization.py b/onionperf/visualization.py
index f5bc03f..2cd3161 100644
--- a/onionperf/visualization.py
+++ b/onionperf/visualization.py
@@ -147,7 +147,7 @@ class TGenVisualization(Visualization):
 stream["error_code"] = "/".join(error_code_parts)
 
 if "filters" in analysis.json_db.keys() and 
analysis.json_db["filters"]["tor/circuits"]:
-   if tor_circuit and "filtered" not in 
tor_circuit.keys():
+   if tor_circuit and "filtered_out" not in 
tor_circuit.keys():
streams.append(stream)
 else:
streams.append(stream)



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Attempt to clean up CHANGELOG.md and README.md.

2020-09-18 Thread karsten
commit f063b636fd26a938a52508424d7abe62cd45ccf0
Author: Karsten Loesing 
Date:   Wed Sep 16 12:26:56 2020 +0200

Attempt to clean up CHANGELOG.md and README.md.
---
 CHANGELOG.md | 12 +---
 README.md| 14 +-
 2 files changed, 10 insertions(+), 16 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7d5005..13fb202 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,13 +1,11 @@
-# Changes in version 0.7 - 2020-??-??
+# Changes in version 0.8 - 2020-09-16
 
  - Add a new `onionperf filter` mode that takes an OnionPerf analysis
results file or directory as input, applies filters, and produces
-   new OnionPerf analysis results file(s) as output.
- - Bump the analysis version number to 4.0 for new analyses and analyses
-   produced by the `onionperf filter` mode
- - Analyses produced by the `onionperf filter` mode have additional filter
-   metadata defined in a new 'filters' field, and an optional 'filtered\_out'
-   field per tor circuit
+   new OnionPerf analysis results file(s) as output. Bump the analysis
+   version number to 4.0 to include additional filter metadata defined
+   in a 'filters' field and an optional 'filtered\_out' field per Tor
+   circuit. Implements #33260.
 
 # Changes in version 0.6 - 2020-08-08
 
diff --git a/README.md b/README.md
index 13a9eee..6e7684f 100644
--- a/README.md
+++ b/README.md
@@ -252,31 +252,27 @@ OnionPerf's `analyze` mode has several command-line 
parameters for customizing t
 ```shell
 onionperf analyze --help
 ```
+
 ### Filtering measurement results
 
-The `filter` subcommand is typically used in combination with the `visualize` 
subcommand. The workflow is to filter out any Tor streams/circuits that are not 
desired then visualize only those measurements with an existing mapping between 
TGen transfers/streams and Tor streams/circuits.
+The `filter` subcommand can be used to filter out measurement results based on 
given criteria. This subcommand is typically used in combination with the 
`visualize` subcommand. The workflow is to apply one or more filters and then 
visualize only those measurements with an existing mapping between TGen 
transfers/streams and Tor streams/circuits.
 
-Currently, OnionPerf measurement results can be filtered based on Tor relay 
fingerprints, although support for filtering TGen based on transfers/streams 
may be added in the future.
+Currently, OnionPerf measurement results can be filtered based on Tor relay 
fingerprints found in Tor circuits, although support for filtering based on Tor 
streams and/or TGen transfers/streams may be added in the future.
 
-The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs, and outputs new analysis files which include, 
unchanged, the Tor results obtained over a Tor circuit path which includes or 
excludes fingerprints in the input list. All other Tor results are also 
included in the file, but are marked as 'filtered\_out'.
-Filter metadata detailing the filter type and path to the input list used is 
also included in the analysis file.
+The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs and outputs new analysis files with the same contents 
as the input analysis files plus annotations on those Tor circuits that have 
been filtered out. If a directory of analysis files is given to '-i', the 
structure and filenames of that directory are preserved under the path 
specified with '-o'.
 
-For example, the analysis file produced above can be filtered with the 
following command, which retains measurements based on fingerprints contained 
in the file 'fingerprints.txt':
+For example, the analysis file produced above can be filtered with the 
following command, which retains only those Tor circuits with fingerprints 
contained in the file 'fingerprints.txt':
 
 ```shell
 onionperf filter -i onionperf.analysis.json.xz -o 
filtered.onionperf.analysis.json.xz --include-fingerprints fingerprints.txt
 ```
 
-The output analysis file is written to the path specified with `-o`. If 
processing a directory of analysis files, its structure and filenames are 
preserved under the path specified with '-o'.
-Note that while the subcommand filters `tgen` measurements, it leaves `tgen` 
and `tor` summaries in the original analysis file unchanged.
-
 OnionPerf's `filter` command usage can be inspected with:
 
 ```shell
 onionperf filter --help
 ```
 
-
 ### Visualizing measurement results
 
 Step two in the analysis is to process analysis files with OnionPerf's 
`visualize` mode which produces CSV and PDF files as output.



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Fix naming of exclusion list

2020-09-18 Thread karsten
commit d9f8b8fe36fabb30bf96802130a68b9020f6a875
Author: Ana Custura 
Date:   Fri Aug 21 03:07:30 2020 +0100

Fix naming of exclusion list
---
 onionperf/filtering.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 204b161..ab96f1e 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -28,13 +28,13 @@ class Filtering(object):
 self.fingerprints_to_include.append(fingerprint)
 
 def exclude_fingerprints(self, path):
-self.exclude_fingerprints = []
+self.fingerprints_to_exclude = []
 with open(path, 'rt') as f:
 for line in f:
 fingerprint_match = self.fingerprint_pattern.match(line)
 if fingerprint_match:
 fingerprint = fingerprint_match.group(1).upper()
-self.exclude_fingerprints.append(fingerprint)
+self.fingerprints_to_exclude.append(fingerprint)
 
 def apply_filters(self):
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Prevent incorrectly keeping transfers for empty incl./excl. lists

2020-09-18 Thread karsten
commit efe2ef11eb91e803e44229f5ede0209ead0aae21
Author: Ana Custura 
Date:   Sat Aug 29 14:18:01 2020 +0100

Prevent incorrectly keeping transfers for empty incl./excl. lists
---
 onionperf/filtering.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index b431eb9..9e7b34f 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -79,10 +79,10 @@ class Filtering(object):
 fingerprint_match = 
self.fingerprint_pattern.match(long_name)
 if fingerprint_match:
 fingerprint = fingerprint_match.group(1).upper()
-if self.fingerprints_to_include and fingerprint 
not in self.fingerprints_to_include:
+if self.fingerprints_to_include is not None and 
fingerprint not in self.fingerprints_to_include:
 keep = False
 break
-if self.fingerprints_to_exclude and fingerprint in 
self.fingerprints_to_exclude:
+if self.fingerprints_to_exclude is not None and 
fingerprint in self.fingerprints_to_exclude:
 keep = False
 break
 if keep:



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Add new filter mode to filter analysis results.

2020-09-18 Thread karsten
commit dcab214322356db9d975673d19146ff46ec86b4f
Author: Karsten Loesing 
Date:   Tue Aug 18 21:05:45 2020 +0200

Add new filter mode to filter analysis results.

Implements tpo/metrics/onionperf#33260.
---
 CHANGELOG.md   |   6 +++
 onionperf/analysis.py  |  11 ++
 onionperf/filtering.py | 100 +
 onionperf/onionperf|  51 +
 4 files changed, 168 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b8a86ee..c57695e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# Changes in version 0.7 - 2020-??-??
+
+ - Add a new `onionperf filter` mode that takes an OnionPerf analysis
+   results file as input, applies filters, and produces a new
+   OnionPerf analysis results file as output.
+
 # Changes in version 0.6 - 2020-08-08
 
  - Update to TGen 1.0.0, use TGenTools for parsing TGen log files, and
diff --git a/onionperf/analysis.py b/onionperf/analysis.py
index ea07d0e..b2f483f 100644
--- a/onionperf/analysis.py
+++ b/onionperf/analysis.py
@@ -62,6 +62,11 @@ class OPAnalysis(Analysis):
 self.json_db['data'][self.nickname]["tgen"].pop("stream_summary")
 self.did_analysis = True
 
+def set_tgen_transfers(self, node, tgen_transfers):
+self.json_db['data'][node]['tgen']['transfers'] = tgen_transfers
+
+def set_tgen_streams(self, node, tgen_streams):
+self.json_db['data'][node]['tgen']['streams'] = tgen_streams
 
 def save(self, filename=None, output_prefix=os.getcwd(), do_compress=True, 
date_prefix=None):
 if filename is None:
@@ -98,6 +103,12 @@ class OPAnalysis(Analysis):
 except:
 return None
 
+def get_tor_circuits(self, node):
+try:
+return self.json_db['data'][node]['tor']['circuits']
+except:
+return None
+
 def get_tor_streams(self, node):
 try:
 return self.json_db['data'][node]['tor']['streams']
diff --git a/onionperf/filtering.py b/onionperf/filtering.py
new file mode 100644
index 000..435a1bc
--- /dev/null
+++ b/onionperf/filtering.py
@@ -0,0 +1,100 @@
+'''
+  OnionPerf
+  Authored by Rob Jansen, 2015
+  Copyright 2015-2020 The Tor Project
+  See LICENSE for licensing information
+'''
+
+import re
+from onionperf.analysis import OPAnalysis
+
+class Filtering(object):
+
+def __init__(self):
+self.fingerprints_to_include = None
+self.fingerprints_to_exclude = None
+self.fingerprint_pattern = re.compile("\$?([0-9a-fA-F]{40})")
+
+def read_input(self, path):
+self.analysis = OPAnalysis.load(filename=path)
+
+def include_fingerprints(self, path):
+self.fingerprints_to_include = []
+with open(path, 'rt') as f:
+for line in f:
+fingerprint_match = self.fingerprint_pattern.match(line)
+if fingerprint_match:
+fingerprint = fingerprint_match.group(1).upper()
+self.fingerprints_to_include.append(fingerprint)
+
+def exclude_fingerprints(self, path):
+self.exclude_fingerprints = []
+with open(path, 'rt') as f:
+for line in f:
+fingerprint_match = self.fingerprint_pattern.match(line)
+if fingerprint_match:
+fingerprint = fingerprint_match.group(1).upper()
+self.exclude_fingerprints.append(fingerprint)
+
+def apply_filters(self):
+if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
+return
+for source in self.analysis.get_nodes():
+tor_streams_by_source_port = {}
+tor_streams = self.analysis.get_tor_streams(source)
+for tor_stream in tor_streams.values():
+if "source" in tor_stream and ":" in tor_stream["source"]:
+source_port = tor_stream["source"].split(":")[1]
+tor_streams_by_source_port.setdefault(source_port, 
[]).append(tor_stream)
+tor_circuits = self.analysis.get_tor_circuits(source)
+tgen_streams = self.analysis.get_tgen_streams(source)
+tgen_transfers = self.analysis.get_tgen_transfers(source)
+retained_tgen_streams = {}
+retained_tgen_transfers = {}
+while tgen_streams or tgen_transfers:
+stream_id = None
+transfer_id = None
+source_port = None
+unix_ts_end = None
+keep = False
+if tgen_streams:
+stream_id, stream_data = tgen_streams.popitem()
+if "local" in stream_data["transport_info"] and 
len(stream_data["transport_info"]["local"].split(":")) > 2:
+source_port = 
stream_data["transp

[tor-commits] [onionperf/develop] Bump version to 0.8.

2020-09-18 Thread karsten
commit 118d14990ce4821232ed6751c6a3d2b05e872dbe
Author: Karsten Loesing 
Date:   Wed Sep 16 12:29:43 2020 +0200

Bump version to 0.8.
---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index d72b27a..b7b00d0 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ with open('requirements.txt') as f:
 install_requires = f.readlines()
 
 setup(name='OnionPerf',
-  version='0.7',
+  version='0.8',
   description='A utility to monitor, measure, analyze, and visualize the 
performance of Tor and Onion Services',
   author='Rob Jansen',
   url='https://github.com/robgjansen/onionperf/',

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Also accept a directory in `onionperf filter -i`.

2020-09-18 Thread karsten
commit 053da92c69d4b3628f33e1dd610a1bc8f601cbe0
Author: Karsten Loesing 
Date:   Thu Aug 27 09:59:59 2020 +0200

Also accept a directory in `onionperf filter -i`.

And clarify that we're leaving statistics unchanged as part of the
filtering.
---
 CHANGELOG.md   |  4 ++--
 onionperf/filtering.py | 10 +++---
 onionperf/onionperf| 35 ---
 3 files changed, 29 insertions(+), 20 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c57695e..ebd43e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,8 @@
 # Changes in version 0.7 - 2020-??-??
 
  - Add a new `onionperf filter` mode that takes an OnionPerf analysis
-   results file as input, applies filters, and produces a new
-   OnionPerf analysis results file as output.
+   results file or directory as input, applies filters, and produces
+   new OnionPerf analysis results file(s) as output.
 
 # Changes in version 0.6 - 2020-08-08
 
diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index ab96f1e..b431eb9 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -15,9 +15,6 @@ class Filtering(object):
 self.fingerprints_to_exclude = None
 self.fingerprint_pattern = re.compile("\$?([0-9a-fA-F]{40})")
 
-def read_input(self, path):
-self.analysis = OPAnalysis.load(filename=path)
-
 def include_fingerprints(self, path):
 self.fingerprints_to_include = []
 with open(path, 'rt') as f:
@@ -36,7 +33,8 @@ class Filtering(object):
 fingerprint = fingerprint_match.group(1).upper()
 self.fingerprints_to_exclude.append(fingerprint)
 
-def apply_filters(self):
+def apply_filters(self, input_path, output_dir, output_file):
+self.analysis = OPAnalysis.load(filename=input_path)
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
 return
 for source in self.analysis.get_nodes():
@@ -94,7 +92,5 @@ class Filtering(object):
 retained_tgen_transfers[transfer_id] = transfer_data
 self.analysis.set_tgen_streams(source, retained_tgen_streams)
 self.analysis.set_tgen_transfers(source, retained_tgen_transfers)
-
-def write_output(self, path):
-self.analysis.save(filename=path)
+self.analysis.save(filename=output_file, output_prefix=output_dir)
 
diff --git a/onionperf/onionperf b/onionperf/onionperf
index 96c6869..7c16aea 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -76,8 +76,11 @@ Analyze Tor and TGen output
 """
 
 DESC_FILTER = """
-Takes an OnionPerf analysis results file as input, applies filters,
-and produces a new OnionPerf analysis results file as output.
+Takes an OnionPerf analysis results file or directory as input, applies 
filters,
+and produces new OnionPerf analysis results file(s) as output.
+
+This subcommand only filters measurements in `data/[source]/tgen/transfers`
+and `data/[source]/tgen/streams`, but leaves any summaries unchanged.
 """
 HELP_FILTER = """
 Filter OnionPerf analysis results
@@ -295,7 +298,8 @@ files generated by this script will be written""",
 filter_parser.set_defaults(func=filter, formatter_class=my_formatter_class)
 
 filter_parser.add_argument('-i', '--input',
-help="""read the OnionPerf analysis results at PATH as input""",
+help="""a file or directory PATH from which OnionPerf analysis results
+files are read""",
 metavar="PATH", required="True",
 action="store", dest="input")
 
@@ -314,8 +318,8 @@ files generated by this script will be written""",
 default=None)
 
 filter_parser.add_argument('-o', '--output',
-help="""write the filtered output OnionPerf analysis results file to
-PATH""",
+help="""a file or directory PATH where filtered output OnionPerf
+analysis results files are written""",
 metavar="PATH", required="True",
 action="store", dest="output")
 
@@ -439,17 +443,26 @@ def analyze(args):
 def filter(args):
 from onionperf.filtering import Filtering
 
-p = os.path.abspath(os.path.expanduser(args.input))
-if not os.path.exists(p):
-raise argparse.ArgumentTypeError("path '%s' does not exist" % 
args.input)
+input_path = os.path.abspath(os.path.expanduser(args.input))
+if not os.path.exists(input_path):
+raise argparse.ArgumentTypeError("input path '%s' does not exist" % 
args.input)
+output_path = os.path.abspath(os.path.expanduser(args.output))
+if os.path.exists(output_path)

[tor-commits] [onionperf/master] Fix matching of tor_circuit ids

2020-09-18 Thread karsten
commit 26ff79a3e57f1dfb0c961cceb167534bb39d17f3
Author: Ana Custura 
Date:   Fri Aug 21 02:14:32 2020 +0100

Fix matching of tor_circuit ids
---
 onionperf/filtering.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 435a1bc..204b161 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -73,7 +73,7 @@ class Filtering(object):
 for tor_stream in tor_streams_by_source_port[source_port]:
 if abs(unix_ts_end - tor_stream["unix_ts_end"]) < 
150.0:
 circuit_id = tor_stream["circuit_id"]
-if circuit_id and circuit_id in tor_circuits:
+if circuit_id and str(circuit_id) in tor_circuits:
 tor_circuit = tor_circuits[circuit_id]
 path = tor_circuit["path"]
 keep = True



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Add path validation to filter interface

2020-09-18 Thread karsten
commit 1a81b3dff756cf197d661f931485443d1454c7c4
Author: Ana Custura 
Date:   Fri Aug 21 01:38:01 2020 +0100

Add path validation to filter interface
---
 onionperf/onionperf | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/onionperf/onionperf b/onionperf/onionperf
index b1e7bd3..96c6869 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -439,6 +439,9 @@ def analyze(args):
 def filter(args):
 from onionperf.filtering import Filtering
 
+p = os.path.abspath(os.path.expanduser(args.input))
+if not os.path.exists(p):
+raise argparse.ArgumentTypeError("path '%s' does not exist" % 
args.input)
 filtering = Filtering()
 filtering.read_input(args.input)
 if args.include_fingerprints is not None:



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Change filter mode to filter Tor circuits.

2020-09-16 Thread karsten
commit 9d0c80561b84905e72b41758dfcb7b712f5f407f
Author: Karsten Loesing 
Date:   Mon Aug 31 11:30:53 2020 +0200

Change filter mode to filter Tor circuits.

This new filter mode removes Tor circuits that don't match the
provided fingerprints and leaves TGen transfers/streams untouched. At
the same time the visualize mode only includes TGen transfers/streams
with an existing mapping between TGen transfers/streams and Tor
streams/circuits.

This patch changes the default behavior of the visualize mode. The
original behavior of visualizing TGen transfers/streams *without* an
existing mapping to Tor streams/circuits can be selected with the
--outer-join switch, even though that's rather an edge use case.

Another minor change is that the filtered analysis files is not
written with sort_keys=True anymore, which would have produced a newly
sorted file with keys in alphabetic order rather than the original
insert order. The result is an actually useful diff.
---
 onionperf/analysis.py  | 13 +---
 onionperf/filtering.py | 49 --
 onionperf/onionperf| 24 +--
 onionperf/visualization.py | 33 ---
 4 files changed, 53 insertions(+), 66 deletions(-)

diff --git a/onionperf/analysis.py b/onionperf/analysis.py
index b2f483f..49c109c 100644
--- a/onionperf/analysis.py
+++ b/onionperf/analysis.py
@@ -62,13 +62,7 @@ class OPAnalysis(Analysis):
 self.json_db['data'][self.nickname]["tgen"].pop("stream_summary")
 self.did_analysis = True
 
-def set_tgen_transfers(self, node, tgen_transfers):
-self.json_db['data'][node]['tgen']['transfers'] = tgen_transfers
-
-def set_tgen_streams(self, node, tgen_streams):
-self.json_db['data'][node]['tgen']['streams'] = tgen_streams
-
-def save(self, filename=None, output_prefix=os.getcwd(), do_compress=True, 
date_prefix=None):
+def save(self, filename=None, output_prefix=os.getcwd(), do_compress=True, 
date_prefix=None, sort_keys=True):
 if filename is None:
 base_filename = "onionperf.analysis.json.xz"
 if date_prefix is not None:
@@ -85,7 +79,7 @@ class OPAnalysis(Analysis):
 logging.info("saving analysis results to {0}".format(filepath))
 
 outf = util.FileWritable(filepath, do_compress=do_compress)
-json.dump(self.json_db, outf, sort_keys=True, separators=(',', ': '), 
indent=2)
+json.dump(self.json_db, outf, sort_keys=sort_keys, separators=(',', ': 
'), indent=2)
 outf.close()
 
 logging.info("done!")
@@ -109,6 +103,9 @@ class OPAnalysis(Analysis):
 except:
 return None
 
+def set_tor_circuits(self, node, tor_circuits):
+self.json_db['data'][node]['tor']['circuits'] = tor_circuits
+
 def get_tor_streams(self, node):
 try:
 return self.json_db['data'][node]['tor']['streams']
diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 9e7b34f..1b614d6 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -38,41 +38,11 @@ class Filtering(object):
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
 return
 for source in self.analysis.get_nodes():
-tor_streams_by_source_port = {}
-tor_streams = self.analysis.get_tor_streams(source)
-for tor_stream in tor_streams.values():
-if "source" in tor_stream and ":" in tor_stream["source"]:
-source_port = tor_stream["source"].split(":")[1]
-tor_streams_by_source_port.setdefault(source_port, 
[]).append(tor_stream)
 tor_circuits = self.analysis.get_tor_circuits(source)
-tgen_streams = self.analysis.get_tgen_streams(source)
-tgen_transfers = self.analysis.get_tgen_transfers(source)
-retained_tgen_streams = {}
-retained_tgen_transfers = {}
-while tgen_streams or tgen_transfers:
-stream_id = None
-transfer_id = None
-source_port = None
-unix_ts_end = None
+filtered_circuit_ids = []
+for circuit_id, tor_circuit in tor_circuits.items():
 keep = False
-if tgen_streams:
-stream_id, stream_data = tgen_streams.popitem()
-if "local" in stream_data["transport_info"] and 
len(stream_data["transport_info"]["local"].split(":")) > 2:
-source_port = 
stream_data["transport_info"]["local"].split(":")[2]
-if "unix_ts_end" in stream_data:
-

[tor-commits] [onionperf/develop] Merge branch 'acute-task-33260-4' into develop

2020-09-16 Thread karsten
commit 76517ac669f3e7575965662507ab970b41395324
Merge: c8275b2 f063b63
Author: Karsten Loesing 
Date:   Wed Sep 16 12:28:19 2020 +0200

Merge branch 'acute-task-33260-4' into develop

 CHANGELOG.md   |   9 +
 README.md  |  23 ++
 onionperf/analysis.py  |  18 +-
 onionperf/filtering.py |  75 ++
 onionperf/onionperf|  73 ++
 onionperf/visualization.py |  31 ++-
 schema/onionperf-4.0.json  | 577 +
 7 files changed, 793 insertions(+), 13 deletions(-)

diff --cc CHANGELOG.md
index 80fb14d,13fb202..2c1f5ca
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@@ -1,13 -1,12 +1,22 @@@
+ # Changes in version 0.8 - 2020-09-16
+ 
+  - Add a new `onionperf filter` mode that takes an OnionPerf analysis
+results file or directory as input, applies filters, and produces
+new OnionPerf analysis results file(s) as output. Bump the analysis
+version number to 4.0 to include additional filter metadata defined
+in a 'filters' field and an optional 'filtered\_out' field per Tor
+circuit. Implements #33260.
+ 
 +# Changes in version 0.7 - 2020-09-01
 +
 + - Add `onionperf measure --drop-guards` parameter to use and drop
 +   guards and circuit build timeouts every given number of hours, if
 +   supported by the Tor version. Implements #33399.
 + - Remove the `onionperf measure --oneshot` switch and replace it with
 +   new switches `--tgen-pause-initial`, `--tgen-pause-between`,
 +   `--tgen-transfer-size`, and `--tgen-num-transfers ` to further
 +   configure the generated TGen model. Implemets #33432.
 +
  # Changes in version 0.6 - 2020-08-08
  
   - Update to TGen 1.0.0, use TGenTools for parsing TGen log files, and

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Order analysis and filter keys, authored by Karsten

2020-09-16 Thread karsten
commit 7b0c91f1983ab21f39f37b83d3a58db5a4d11a8f
Author: Ana Custura 
Date:   Thu Sep 10 12:37:00 2020 +0100

Order analysis and filter keys, authored by Karsten
---
 onionperf/filtering.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index c008c03..7ef6168 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -65,10 +65,12 @@ class Filtering(object):
 break
 if not keep:
 tor_circuits[circuit_id]["filtered"] = True
+tor_circuits[circuit_id] = 
dict(sorted(tor_circuit.items()))
 
 def apply_filters(self, input_path, output_dir, output_file):
 self.analysis = OPAnalysis.load(filename=input_path)
 self.filter_tor_circuits(self.analysis)
 self.analysis.json_db["filters"] = self.filters
+self.analysis.json_db = dict(sorted(self.analysis.json_db.items()))
 self.analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Update README and CHANGELOG

2020-09-16 Thread karsten
commit 1b638e34cb0476148dcf93b43b58b54a30c27428
Author: Ana Custura 
Date:   Thu Sep 10 14:46:10 2020 +0100

Update README and CHANGELOG
---
 CHANGELOG.md |  5 +
 README.md| 10 +++---
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ebd43e1..a7d5005 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,11 @@
  - Add a new `onionperf filter` mode that takes an OnionPerf analysis
results file or directory as input, applies filters, and produces
new OnionPerf analysis results file(s) as output.
+ - Bump the analysis version number to 4.0 for new analyses and analyses
+   produced by the `onionperf filter` mode
+ - Analyses produced by the `onionperf filter` mode have additional filter
+   metadata defined in a new 'filters' field, and an optional 'filtered\_out'
+   field per tor circuit
 
 # Changes in version 0.6 - 2020-08-08
 
diff --git a/README.md b/README.md
index 224972c..13a9eee 100644
--- a/README.md
+++ b/README.md
@@ -254,10 +254,12 @@ onionperf analyze --help
 ```
 ### Filtering measurement results
 
-OnionPerf measurement results can be filtered based on Tor relay fingerprints.
-The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs, and outputs new analysis files containing only the 
`tgen` results obtained over a Tor circuit path which includes or excludes 
fingerprints in the input list.
+The `filter` subcommand is typically used in combination with the `visualize` 
subcommand. The workflow is to filter out any Tor streams/circuits that are not 
desired then visualize only those measurements with an existing mapping between 
TGen transfers/streams and Tor streams/circuits.
 
-Where excluding fingerprints, if ANY relay fingerprint is matched, the 
measurement is discarded. Where including fingerprints, ALL relay fingerprints 
in a path must match for the measurement to be retained.
+Currently, OnionPerf measurement results can be filtered based on Tor relay 
fingerprints, although support for filtering TGen based on transfers/streams 
may be added in the future.
+
+The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs, and outputs new analysis files which include, 
unchanged, the Tor results obtained over a Tor circuit path which includes or 
excludes fingerprints in the input list. All other Tor results are also 
included in the file, but are marked as 'filtered\_out'.
+Filter metadata detailing the filter type and path to the input list used is 
also included in the analysis file.
 
 For example, the analysis file produced above can be filtered with the 
following command, which retains measurements based on fingerprints contained 
in the file 'fingerprints.txt':
 
@@ -290,6 +292,8 @@ As a result, two files are written to the current working 
directory:
 - `onionperf.viz.$datetime.csv` contains visualized data in a CSV file format; 
and
 - `onionperf.viz.$datetime.pdf` contains visualizations in a PDF file format.
 
+For analysis files containing tor circuit filters, only measurements with an 
existing mapping between TGen transfers/streams Tor streams/circuits which have 
not been marked as 'filtered\_out' are visualized.
+
 Similar to the other modes, OnionPerf's `visualize` mode has command-line 
parameters for customizing the visualization step:
 
 ```shell



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Attempt to clean up CHANGELOG.md and README.md.

2020-09-16 Thread karsten
commit f063b636fd26a938a52508424d7abe62cd45ccf0
Author: Karsten Loesing 
Date:   Wed Sep 16 12:26:56 2020 +0200

Attempt to clean up CHANGELOG.md and README.md.
---
 CHANGELOG.md | 12 +---
 README.md| 14 +-
 2 files changed, 10 insertions(+), 16 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7d5005..13fb202 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,13 +1,11 @@
-# Changes in version 0.7 - 2020-??-??
+# Changes in version 0.8 - 2020-09-16
 
  - Add a new `onionperf filter` mode that takes an OnionPerf analysis
results file or directory as input, applies filters, and produces
-   new OnionPerf analysis results file(s) as output.
- - Bump the analysis version number to 4.0 for new analyses and analyses
-   produced by the `onionperf filter` mode
- - Analyses produced by the `onionperf filter` mode have additional filter
-   metadata defined in a new 'filters' field, and an optional 'filtered\_out'
-   field per tor circuit
+   new OnionPerf analysis results file(s) as output. Bump the analysis
+   version number to 4.0 to include additional filter metadata defined
+   in a 'filters' field and an optional 'filtered\_out' field per Tor
+   circuit. Implements #33260.
 
 # Changes in version 0.6 - 2020-08-08
 
diff --git a/README.md b/README.md
index 13a9eee..6e7684f 100644
--- a/README.md
+++ b/README.md
@@ -252,31 +252,27 @@ OnionPerf's `analyze` mode has several command-line 
parameters for customizing t
 ```shell
 onionperf analyze --help
 ```
+
 ### Filtering measurement results
 
-The `filter` subcommand is typically used in combination with the `visualize` 
subcommand. The workflow is to filter out any Tor streams/circuits that are not 
desired then visualize only those measurements with an existing mapping between 
TGen transfers/streams and Tor streams/circuits.
+The `filter` subcommand can be used to filter out measurement results based on 
given criteria. This subcommand is typically used in combination with the 
`visualize` subcommand. The workflow is to apply one or more filters and then 
visualize only those measurements with an existing mapping between TGen 
transfers/streams and Tor streams/circuits.
 
-Currently, OnionPerf measurement results can be filtered based on Tor relay 
fingerprints, although support for filtering TGen based on transfers/streams 
may be added in the future.
+Currently, OnionPerf measurement results can be filtered based on Tor relay 
fingerprints found in Tor circuits, although support for filtering based on Tor 
streams and/or TGen transfers/streams may be added in the future.
 
-The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs, and outputs new analysis files which include, 
unchanged, the Tor results obtained over a Tor circuit path which includes or 
excludes fingerprints in the input list. All other Tor results are also 
included in the file, but are marked as 'filtered\_out'.
-Filter metadata detailing the filter type and path to the input list used is 
also included in the analysis file.
+The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs and outputs new analysis files with the same contents 
as the input analysis files plus annotations on those Tor circuits that have 
been filtered out. If a directory of analysis files is given to '-i', the 
structure and filenames of that directory are preserved under the path 
specified with '-o'.
 
-For example, the analysis file produced above can be filtered with the 
following command, which retains measurements based on fingerprints contained 
in the file 'fingerprints.txt':
+For example, the analysis file produced above can be filtered with the 
following command, which retains only those Tor circuits with fingerprints 
contained in the file 'fingerprints.txt':
 
 ```shell
 onionperf filter -i onionperf.analysis.json.xz -o 
filtered.onionperf.analysis.json.xz --include-fingerprints fingerprints.txt
 ```
 
-The output analysis file is written to the path specified with `-o`. If 
processing a directory of analysis files, its structure and filenames are 
preserved under the path specified with '-o'.
-Note that while the subcommand filters `tgen` measurements, it leaves `tgen` 
and `tor` summaries in the original analysis file unchanged.
-
 OnionPerf's `filter` command usage can be inspected with:
 
 ```shell
 onionperf filter --help
 ```
 
-
 ### Visualizing measurement results
 
 Step two in the analysis is to process analysis files with OnionPerf's 
`visualize` mode which produces CSV and PDF files as output.



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Add new filter mode to filter analysis results.

2020-09-16 Thread karsten
commit dcab214322356db9d975673d19146ff46ec86b4f
Author: Karsten Loesing 
Date:   Tue Aug 18 21:05:45 2020 +0200

Add new filter mode to filter analysis results.

Implements tpo/metrics/onionperf#33260.
---
 CHANGELOG.md   |   6 +++
 onionperf/analysis.py  |  11 ++
 onionperf/filtering.py | 100 +
 onionperf/onionperf|  51 +
 4 files changed, 168 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b8a86ee..c57695e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# Changes in version 0.7 - 2020-??-??
+
+ - Add a new `onionperf filter` mode that takes an OnionPerf analysis
+   results file as input, applies filters, and produces a new
+   OnionPerf analysis results file as output.
+
 # Changes in version 0.6 - 2020-08-08
 
  - Update to TGen 1.0.0, use TGenTools for parsing TGen log files, and
diff --git a/onionperf/analysis.py b/onionperf/analysis.py
index ea07d0e..b2f483f 100644
--- a/onionperf/analysis.py
+++ b/onionperf/analysis.py
@@ -62,6 +62,11 @@ class OPAnalysis(Analysis):
 self.json_db['data'][self.nickname]["tgen"].pop("stream_summary")
 self.did_analysis = True
 
+def set_tgen_transfers(self, node, tgen_transfers):
+self.json_db['data'][node]['tgen']['transfers'] = tgen_transfers
+
+def set_tgen_streams(self, node, tgen_streams):
+self.json_db['data'][node]['tgen']['streams'] = tgen_streams
 
 def save(self, filename=None, output_prefix=os.getcwd(), do_compress=True, 
date_prefix=None):
 if filename is None:
@@ -98,6 +103,12 @@ class OPAnalysis(Analysis):
 except:
 return None
 
+def get_tor_circuits(self, node):
+try:
+return self.json_db['data'][node]['tor']['circuits']
+except:
+return None
+
 def get_tor_streams(self, node):
 try:
 return self.json_db['data'][node]['tor']['streams']
diff --git a/onionperf/filtering.py b/onionperf/filtering.py
new file mode 100644
index 000..435a1bc
--- /dev/null
+++ b/onionperf/filtering.py
@@ -0,0 +1,100 @@
+'''
+  OnionPerf
+  Authored by Rob Jansen, 2015
+  Copyright 2015-2020 The Tor Project
+  See LICENSE for licensing information
+'''
+
+import re
+from onionperf.analysis import OPAnalysis
+
+class Filtering(object):
+
+def __init__(self):
+self.fingerprints_to_include = None
+self.fingerprints_to_exclude = None
+self.fingerprint_pattern = re.compile("\$?([0-9a-fA-F]{40})")
+
+def read_input(self, path):
+self.analysis = OPAnalysis.load(filename=path)
+
+def include_fingerprints(self, path):
+self.fingerprints_to_include = []
+with open(path, 'rt') as f:
+for line in f:
+fingerprint_match = self.fingerprint_pattern.match(line)
+if fingerprint_match:
+fingerprint = fingerprint_match.group(1).upper()
+self.fingerprints_to_include.append(fingerprint)
+
+def exclude_fingerprints(self, path):
+self.exclude_fingerprints = []
+with open(path, 'rt') as f:
+for line in f:
+fingerprint_match = self.fingerprint_pattern.match(line)
+if fingerprint_match:
+fingerprint = fingerprint_match.group(1).upper()
+self.exclude_fingerprints.append(fingerprint)
+
+def apply_filters(self):
+if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
+return
+for source in self.analysis.get_nodes():
+tor_streams_by_source_port = {}
+tor_streams = self.analysis.get_tor_streams(source)
+for tor_stream in tor_streams.values():
+if "source" in tor_stream and ":" in tor_stream["source"]:
+source_port = tor_stream["source"].split(":")[1]
+tor_streams_by_source_port.setdefault(source_port, 
[]).append(tor_stream)
+tor_circuits = self.analysis.get_tor_circuits(source)
+tgen_streams = self.analysis.get_tgen_streams(source)
+tgen_transfers = self.analysis.get_tgen_transfers(source)
+retained_tgen_streams = {}
+retained_tgen_transfers = {}
+while tgen_streams or tgen_transfers:
+stream_id = None
+transfer_id = None
+source_port = None
+unix_ts_end = None
+keep = False
+if tgen_streams:
+stream_id, stream_data = tgen_streams.popitem()
+if "local" in stream_data["transport_info"] and 
len(stream_data["transport_info"]["local"].split(":")) > 2:
+source_port = 
stream_data["transp

[tor-commits] [onionperf/develop] Move filters and filter metadata to analysis files

2020-09-16 Thread karsten
commit 95b749a8fc690825c0a828b8473c58faea7ad912
Author: Ana Custura 
Date:   Thu Sep 10 01:51:54 2020 +0100

Move filters and filter metadata to analysis files
---
 onionperf/filtering.py | 25 ++---
 onionperf/onionperf|  9 +
 onionperf/visualization.py | 14 +-
 3 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 1b614d6..c008c03 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -7,6 +7,7 @@
 
 import re
 from onionperf.analysis import OPAnalysis
+from collections import defaultdict
 
 class Filtering(object):
 
@@ -14,9 +15,11 @@ class Filtering(object):
 self.fingerprints_to_include = None
 self.fingerprints_to_exclude = None
 self.fingerprint_pattern = re.compile("\$?([0-9a-fA-F]{40})")
+self.filters = defaultdict(list)
 
 def include_fingerprints(self, path):
 self.fingerprints_to_include = []
+self.fingerprints_to_include_path = path
 with open(path, 'rt') as f:
 for line in f:
 fingerprint_match = self.fingerprint_pattern.match(line)
@@ -26,6 +29,7 @@ class Filtering(object):
 
 def exclude_fingerprints(self, path):
 self.fingerprints_to_exclude = []
+self.fingerprints_to_exclude_path = path
 with open(path, 'rt') as f:
 for line in f:
 fingerprint_match = self.fingerprint_pattern.match(line)
@@ -33,12 +37,16 @@ class Filtering(object):
 fingerprint = fingerprint_match.group(1).upper()
 self.fingerprints_to_exclude.append(fingerprint)
 
-def apply_filters(self, input_path, output_dir, output_file):
-self.analysis = OPAnalysis.load(filename=input_path)
+def filter_tor_circuits(self, analysis):
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
 return
-for source in self.analysis.get_nodes():
-tor_circuits = self.analysis.get_tor_circuits(source)
+self.filters["tor/circuits"] = []
+if self.fingerprints_to_include:
+   self.filters["tor/circuits"].append({"name": 
"include_fingerprints", "filepath": self.fingerprints_to_include_path })
+if self.fingerprints_to_exclude:
+   self.filters["tor/circuits"].append({"name": 
"exclude_fingerprints", "filepath": self.fingerprints_to_exclude_path })
+for source in analysis.get_nodes():
+tor_circuits = analysis.get_tor_circuits(source)
 filtered_circuit_ids = []
 for circuit_id, tor_circuit in tor_circuits.items():
 keep = False
@@ -56,8 +64,11 @@ class Filtering(object):
 keep = False
 break
 if not keep:
-filtered_circuit_ids.append(circuit_id)
-for circuit_id in filtered_circuit_ids:
-del(tor_circuits[circuit_id])
+tor_circuits[circuit_id]["filtered"] = True
+
+def apply_filters(self, input_path, output_dir, output_file):
+self.analysis = OPAnalysis.load(filename=input_path)
+self.filter_tor_circuits(self.analysis)
+self.analysis.json_db["filters"] = self.filters
 self.analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
 
diff --git a/onionperf/onionperf b/onionperf/onionperf
index 1efa8cb..108af4e 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -342,13 +342,6 @@ files generated by this script will be written""",
 required="True",
 action=PathStringArgsAction, dest="datasets")
 
-visualize_parser.add_argument('--outer-join',
-help="""Include measurements without an existing mapping between TGen
-transfers/streams and Tor streams/circuits, which is the
-equivalent of an outer join in the database sense""",
-action="store_true", dest="outer_join",
-default=False)
-
 visualize_parser.add_argument('-p', '--prefix',
 help="a STRING filename prefix for graphs we generate",
 metavar="STRING", type=str,
@@ -489,7 +482,7 @@ def visualize(args):
 if analysis is not None:
analyses.append(analysis)
 tgen_viz.add_dataset(analyses, label)
-tgen_viz.plot_all(args.prefix, outer_join=args.outer_join)
+tgen_viz.plot_all(args.prefix)
 
 def type_nonnegative_integer(value):
 i = int(value)
diff --git a/onionperf/visualization.py b/onionperf/visualization.py
index 0f69879..f5bc03f 100644
--- a/onionperf/visualization.py
+++ b/onionperf/visualization.py
@@ -31,11 +31,11 @@ class Visualization(object, metaclass=ABCMeta):
 
 class TGenVisualization(Visualization):
 
-def plot_all(self, output_prefix, outer_join=False):
+def plot_all(self, output_prefix):
 if len(self.datasets) > 0:
 

[tor-commits] [onionperf/develop] Add JSON schema for analysis version 4.0.

2020-09-16 Thread karsten
commit a4a4dcc97d07b2668fbab593165119bd52f694ed
Author: Karsten Loesing 
Date:   Wed Sep 16 11:19:58 2020 +0200

Add JSON schema for analysis version 4.0.
---
 schema/onionperf-4.0.json | 577 ++
 1 file changed, 577 insertions(+)

diff --git a/schema/onionperf-4.0.json b/schema/onionperf-4.0.json
new file mode 100644
index 000..3c4dba3
--- /dev/null
+++ b/schema/onionperf-4.0.json
@@ -0,0 +1,577 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema;,
+  "$id": 
"https://gitlab.torproject.org/tpo/metrics/onionperf/-/raw/master/schema/onionperf-4.0.json;,
+  "type": "object",
+  "title": "OnionPerf analysis JSON file format 4.0",
+  "required": [
+"data",
+"type",
+"version"
+  ],
+  "properties": {
+"data": {
+  "type": "object",
+  "title": "Measurement data by source name",
+  "propertyNames": {
+"pattern": "^[A-Za-z0-9-]+$"
+  },
+  "additionalProperties": {
+"type": "object",
+"title": "Measurement data from a single source",
+"required": [
+  "measurement_ip",
+  "tgen",
+  "tor"
+],
+"properties": {
+  "measurement_ip": {
+"type": "string",
+"title": "Public IP address of the measuring host."
+  },
+  "tgen": {
+"type": "object",
+"title": "Measurement data obtained from client-side TGen logs",
+"required": [
+  "streams"
+],
+"properties": {
+  "streams": {
+"type": "object",
+"title": "Measurement data, by TGen stream identifier",
+"additionalProperties": {
+  "type": "object",
+  "title": "Information on a single measurement, obtained from 
a single [stream-success] or [stream-error] log message (except for 
elapsed_seconds)",
+  "required": [
+"byte_info",
+"is_complete",
+"is_error",
+"is_success",
+"stream_id",
+"stream_info",
+"time_info",
+"transport_info",
+"unix_ts_end",
+"unix_ts_start"
+  ],
+  "properties": {
+"byte_info": {
+  "type": "object",
+  "title": "Information on sent and received bytes",
+  "required": [
+"payload-bytes-recv",
+"payload-bytes-send",
+"payload-progress-recv",
+"payload-progress-send",
+"total-bytes-recv",
+"total-bytes-send"
+  ],
+  "properties": {
+"payload-bytes-recv": {
+  "type": "string",
+  "pattern": "^[0-9]+$",
+  "title": "Number of payload bytes received"
+},
+"payload-bytes-send": {
+  "type": "string",
+  "pattern": "^[0-9]+$",
+  "title": "Number of payload bytes sent"
+},
+"payload-progress-recv": {
+  "type": "string",
+  "pattern": "^[0-9]+\\.[0-9]+%$",
+  "title": "Progress of receiving payload in percent"
+},
+"payload-progress-send": {
+  "type": "string",
+  "pattern": "^[0-9]+\\.[0-9]+%$",
+  "title": "Progress of sending payload in percent"
+},
+   

[tor-commits] [onionperf/develop] Append to existing "filters" field.

2020-09-16 Thread karsten
commit c41c0b74c47442dc6f6d23f49a012729f71aee49
Author: Karsten Loesing 
Date:   Wed Sep 16 12:01:07 2020 +0200

Append to existing "filters" field.

Before this change we would have replaced an existing "filters" field
with the filters applied in this run. But if the user runs `onionperf
filter` more than once to apply different filters, we'll want to list
all filters in the "filters" field.

This change also removes a couple of `self.`s, in particular
`self.analysis` and `self.filters`, because one Filtering object can
apply filters to more than one analysis.
---
 onionperf/filtering.py | 20 +---
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index e8ea164..15d9c19 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -7,7 +7,6 @@
 
 import re
 from onionperf.analysis import OPAnalysis
-from collections import defaultdict
 
 class Filtering(object):
 
@@ -15,7 +14,6 @@ class Filtering(object):
 self.fingerprints_to_include = None
 self.fingerprints_to_exclude = None
 self.fingerprint_pattern = re.compile("\$?([0-9a-fA-F]{40})")
-self.filters = defaultdict(list)
 
 def include_fingerprints(self, path):
 self.fingerprints_to_include = []
@@ -40,11 +38,12 @@ class Filtering(object):
 def filter_tor_circuits(self, analysis):
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
 return
-self.filters["tor/circuits"] = []
+filters = analysis.json_db.setdefault("filters", {})
+tor_circuits_filters = filters.setdefault("tor/circuits", [])
 if self.fingerprints_to_include:
-   self.filters["tor/circuits"].append({"name": 
"include_fingerprints", "filepath": self.fingerprints_to_include_path })
+   tor_circuits_filters.append({"name": "include_fingerprints", 
"filepath": self.fingerprints_to_include_path })
 if self.fingerprints_to_exclude:
-   self.filters["tor/circuits"].append({"name": 
"exclude_fingerprints", "filepath": self.fingerprints_to_exclude_path })
+   tor_circuits_filters.append({"name": "exclude_fingerprints", 
"filepath": self.fingerprints_to_exclude_path })
 for source in analysis.get_nodes():
 tor_circuits = analysis.get_tor_circuits(source)
 filtered_circuit_ids = []
@@ -68,10 +67,9 @@ class Filtering(object):
 tor_circuits[circuit_id] = 
dict(sorted(tor_circuit.items()))
 
 def apply_filters(self, input_path, output_dir, output_file):
-self.analysis = OPAnalysis.load(filename=input_path)
-self.filter_tor_circuits(self.analysis)
-self.analysis.json_db["filters"] = self.filters
-self.analysis.json_db["version"] = '4.0'
-self.analysis.json_db = dict(sorted(self.analysis.json_db.items()))
-self.analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
+analysis = OPAnalysis.load(filename=input_path)
+self.filter_tor_circuits(analysis)
+analysis.json_db["version"] = '4.0'
+analysis.json_db = dict(sorted(analysis.json_db.items()))
+analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Bump analysis version to 4.0

2020-09-16 Thread karsten
commit d0660c1847b46863783b7006efdac4df8103f123
Author: Ana Custura 
Date:   Thu Sep 10 12:54:41 2020 +0100

Bump analysis version to 4.0
---
 onionperf/analysis.py  | 4 ++--
 onionperf/filtering.py | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/onionperf/analysis.py b/onionperf/analysis.py
index 49c109c..907d451 100644
--- a/onionperf/analysis.py
+++ b/onionperf/analysis.py
@@ -24,7 +24,7 @@ class OPAnalysis(Analysis):
 
 def __init__(self, nickname=None, ip_address=None):
 super().__init__(nickname, ip_address)
-self.json_db = {'type': 'onionperf', 'version': '3.0', 'data': {}}
+self.json_db = {'type': 'onionperf', 'version': '4.0', 'data': {}}
 self.torctl_filepaths = []
 
 def add_torctl_file(self, filepath):
@@ -133,7 +133,7 @@ class OPAnalysis(Analysis):
 if 'type' not in db or 'version' not in db:
 logging.warning("'type' or 'version' not present in database")
 return None
-elif db['type'] != 'onionperf' or str(db['version']) >= '4.':
+elif db['type'] != 'onionperf' or str(db['version']) >= '5.':
 logging.warning("type or version not supported (type={0}, 
version={1})".format(db['type'], db['version']))
 return None
 else:
diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 0ba9ebc..e8ea164 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -71,6 +71,7 @@ class Filtering(object):
 self.analysis = OPAnalysis.load(filename=input_path)
 self.filter_tor_circuits(self.analysis)
 self.analysis.json_db["filters"] = self.filters
+self.analysis.json_db["version"] = '4.0'
 self.analysis.json_db = dict(sorted(self.analysis.json_db.items()))
 self.analysis.save(filename=output_file, output_prefix=output_dir, 
sort_keys=False)
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Fix matching of tor_circuit ids

2020-09-16 Thread karsten
commit 26ff79a3e57f1dfb0c961cceb167534bb39d17f3
Author: Ana Custura 
Date:   Fri Aug 21 02:14:32 2020 +0100

Fix matching of tor_circuit ids
---
 onionperf/filtering.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 435a1bc..204b161 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -73,7 +73,7 @@ class Filtering(object):
 for tor_stream in tor_streams_by_source_port[source_port]:
 if abs(unix_ts_end - tor_stream["unix_ts_end"]) < 
150.0:
 circuit_id = tor_stream["circuit_id"]
-if circuit_id and circuit_id in tor_circuits:
+if circuit_id and str(circuit_id) in tor_circuits:
 tor_circuit = tor_circuits[circuit_id]
 path = tor_circuit["path"]
 keep = True



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Add path validation to filter interface

2020-09-16 Thread karsten
commit 1a81b3dff756cf197d661f931485443d1454c7c4
Author: Ana Custura 
Date:   Fri Aug 21 01:38:01 2020 +0100

Add path validation to filter interface
---
 onionperf/onionperf | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/onionperf/onionperf b/onionperf/onionperf
index b1e7bd3..96c6869 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -439,6 +439,9 @@ def analyze(args):
 def filter(args):
 from onionperf.filtering import Filtering
 
+p = os.path.abspath(os.path.expanduser(args.input))
+if not os.path.exists(p):
+raise argparse.ArgumentTypeError("path '%s' does not exist" % 
args.input)
 filtering = Filtering()
 filtering.read_input(args.input)
 if args.include_fingerprints is not None:



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Fix naming of exclusion list

2020-09-16 Thread karsten
commit d9f8b8fe36fabb30bf96802130a68b9020f6a875
Author: Ana Custura 
Date:   Fri Aug 21 03:07:30 2020 +0100

Fix naming of exclusion list
---
 onionperf/filtering.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 204b161..ab96f1e 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -28,13 +28,13 @@ class Filtering(object):
 self.fingerprints_to_include.append(fingerprint)
 
 def exclude_fingerprints(self, path):
-self.exclude_fingerprints = []
+self.fingerprints_to_exclude = []
 with open(path, 'rt') as f:
 for line in f:
 fingerprint_match = self.fingerprint_pattern.match(line)
 if fingerprint_match:
 fingerprint = fingerprint_match.group(1).upper()
-self.exclude_fingerprints.append(fingerprint)
+self.fingerprints_to_exclude.append(fingerprint)
 
 def apply_filters(self):
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Clarify filter terminology

2020-09-16 Thread karsten
commit a466221c5b947bb894a36fd3e5472397f1b0b03e
Author: Ana Custura 
Date:   Thu Sep 10 12:41:46 2020 +0100

Clarify filter terminology
---
 onionperf/filtering.py | 2 +-
 onionperf/visualization.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index 7ef6168..0ba9ebc 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -64,7 +64,7 @@ class Filtering(object):
 keep = False
 break
 if not keep:
-tor_circuits[circuit_id]["filtered"] = True
+tor_circuits[circuit_id]["filtered_out"] = True
 tor_circuits[circuit_id] = 
dict(sorted(tor_circuit.items()))
 
 def apply_filters(self, input_path, output_dir, output_file):
diff --git a/onionperf/visualization.py b/onionperf/visualization.py
index f5bc03f..2cd3161 100644
--- a/onionperf/visualization.py
+++ b/onionperf/visualization.py
@@ -147,7 +147,7 @@ class TGenVisualization(Visualization):
 stream["error_code"] = "/".join(error_code_parts)
 
 if "filters" in analysis.json_db.keys() and 
analysis.json_db["filters"]["tor/circuits"]:
-   if tor_circuit and "filtered" not in 
tor_circuit.keys():
+   if tor_circuit and "filtered_out" not in 
tor_circuit.keys():
streams.append(stream)
 else:
streams.append(stream)



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Only try to load analysis files in an input directory

2020-09-16 Thread karsten
commit d21d41e4d504b48ac5617530830da320a89a9eed
Author: Ana Custura 
Date:   Sat Aug 29 15:10:32 2020 +0100

Only try to load analysis files in an input directory
---
 onionperf/onionperf | 11 ++-
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/onionperf/onionperf b/onionperf/onionperf
index 7c16aea..e3f49c8 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -458,11 +458,12 @@ def filter(args):
 output_dir, output_file = os.path.split(output_path)
 filtering.apply_filters(input_path=input_path, output_dir=output_dir, 
output_file=output_file)
 else:
-for dirpath, dirnames, filenames in os.walk(input_path):
-for filename in filenames:
-input_file = os.path.join(dirpath, filename)
-output_dir = os.path.join(output_path, 
os.path.relpath(dirpath, input_path))
-filtering.apply_filters(input_path=input_file, 
output_dir=output_dir, output_file=filename)
+from onionperf import reprocessing
+analyses = reprocessing.collect_logs(input_path, 
'*onionperf.analysis.*')
+for analysis in analyses:
+full_output_path = os.path.join(output_path, 
os.path.relpath(analysis, input_path))
+output_dir, output_file = os.path.split(full_output_path)
+filtering.apply_filters(input_path=analysis, 
output_dir=output_dir, output_file=output_file)
 
 def visualize(args):
 from onionperf.visualization import TGenVisualization



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Add filter section to README.md

2020-09-16 Thread karsten
commit 52506522bc6dc679318e2af801f7c3a94b6947d0
Author: Ana Custura 
Date:   Sat Aug 29 17:01:13 2020 +0100

Add filter section to README.md
---
 README.md | 23 +++
 1 file changed, 23 insertions(+)

diff --git a/README.md b/README.md
index ad53a9e..224972c 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@
 + [Troubleshooting](#troubleshooting)
   * [Analysis](#analysis)
 + [Analyzing measurement results](#analyzing-measurement-results)
++ [Filtering measurement results](#filtering-measurement-results)
 + [Visualizing measurement results](#visualizing-measurement-results)
 + [Interpreting the PDF output format](#interpreting-the-pdf-output-format)
 + [Interpreting the CSV output format](#interpreting-the-csv-output-format)
@@ -251,6 +252,28 @@ OnionPerf's `analyze` mode has several command-line 
parameters for customizing t
 ```shell
 onionperf analyze --help
 ```
+### Filtering measurement results
+
+OnionPerf measurement results can be filtered based on Tor relay fingerprints.
+The `filter` mode takes a list of fingerprints and one or more existing 
analysis files as inputs, and outputs new analysis files containing only the 
`tgen` results obtained over a Tor circuit path which includes or excludes 
fingerprints in the input list.
+
+Where excluding fingerprints, if ANY relay fingerprint is matched, the 
measurement is discarded. Where including fingerprints, ALL relay fingerprints 
in a path must match for the measurement to be retained.
+
+For example, the analysis file produced above can be filtered with the 
following command, which retains measurements based on fingerprints contained 
in the file 'fingerprints.txt':
+
+```shell
+onionperf filter -i onionperf.analysis.json.xz -o 
filtered.onionperf.analysis.json.xz --include-fingerprints fingerprints.txt
+```
+
+The output analysis file is written to the path specified with `-o`. If 
processing a directory of analysis files, its structure and filenames are 
preserved under the path specified with '-o'.
+Note that while the subcommand filters `tgen` measurements, it leaves `tgen` 
and `tor` summaries in the original analysis file unchanged.
+
+OnionPerf's `filter` command usage can be inspected with:
+
+```shell
+onionperf filter --help
+```
+
 
 ### Visualizing measurement results
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Prevent incorrectly keeping transfers for empty incl./excl. lists

2020-09-16 Thread karsten
commit efe2ef11eb91e803e44229f5ede0209ead0aae21
Author: Ana Custura 
Date:   Sat Aug 29 14:18:01 2020 +0100

Prevent incorrectly keeping transfers for empty incl./excl. lists
---
 onionperf/filtering.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index b431eb9..9e7b34f 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -79,10 +79,10 @@ class Filtering(object):
 fingerprint_match = 
self.fingerprint_pattern.match(long_name)
 if fingerprint_match:
 fingerprint = fingerprint_match.group(1).upper()
-if self.fingerprints_to_include and fingerprint 
not in self.fingerprints_to_include:
+if self.fingerprints_to_include is not None and 
fingerprint not in self.fingerprints_to_include:
 keep = False
 break
-if self.fingerprints_to_exclude and fingerprint in 
self.fingerprints_to_exclude:
+if self.fingerprints_to_exclude is not None and 
fingerprint in self.fingerprints_to_exclude:
 keep = False
 break
 if keep:



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Also accept a directory in `onionperf filter -i`.

2020-09-16 Thread karsten
commit 053da92c69d4b3628f33e1dd610a1bc8f601cbe0
Author: Karsten Loesing 
Date:   Thu Aug 27 09:59:59 2020 +0200

Also accept a directory in `onionperf filter -i`.

And clarify that we're leaving statistics unchanged as part of the
filtering.
---
 CHANGELOG.md   |  4 ++--
 onionperf/filtering.py | 10 +++---
 onionperf/onionperf| 35 ---
 3 files changed, 29 insertions(+), 20 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c57695e..ebd43e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,8 @@
 # Changes in version 0.7 - 2020-??-??
 
  - Add a new `onionperf filter` mode that takes an OnionPerf analysis
-   results file as input, applies filters, and produces a new
-   OnionPerf analysis results file as output.
+   results file or directory as input, applies filters, and produces
+   new OnionPerf analysis results file(s) as output.
 
 # Changes in version 0.6 - 2020-08-08
 
diff --git a/onionperf/filtering.py b/onionperf/filtering.py
index ab96f1e..b431eb9 100644
--- a/onionperf/filtering.py
+++ b/onionperf/filtering.py
@@ -15,9 +15,6 @@ class Filtering(object):
 self.fingerprints_to_exclude = None
 self.fingerprint_pattern = re.compile("\$?([0-9a-fA-F]{40})")
 
-def read_input(self, path):
-self.analysis = OPAnalysis.load(filename=path)
-
 def include_fingerprints(self, path):
 self.fingerprints_to_include = []
 with open(path, 'rt') as f:
@@ -36,7 +33,8 @@ class Filtering(object):
 fingerprint = fingerprint_match.group(1).upper()
 self.fingerprints_to_exclude.append(fingerprint)
 
-def apply_filters(self):
+def apply_filters(self, input_path, output_dir, output_file):
+self.analysis = OPAnalysis.load(filename=input_path)
 if self.fingerprints_to_include is None and 
self.fingerprints_to_exclude is None:
 return
 for source in self.analysis.get_nodes():
@@ -94,7 +92,5 @@ class Filtering(object):
 retained_tgen_transfers[transfer_id] = transfer_data
 self.analysis.set_tgen_streams(source, retained_tgen_streams)
 self.analysis.set_tgen_transfers(source, retained_tgen_transfers)
-
-def write_output(self, path):
-self.analysis.save(filename=path)
+self.analysis.save(filename=output_file, output_prefix=output_dir)
 
diff --git a/onionperf/onionperf b/onionperf/onionperf
index 96c6869..7c16aea 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -76,8 +76,11 @@ Analyze Tor and TGen output
 """
 
 DESC_FILTER = """
-Takes an OnionPerf analysis results file as input, applies filters,
-and produces a new OnionPerf analysis results file as output.
+Takes an OnionPerf analysis results file or directory as input, applies 
filters,
+and produces new OnionPerf analysis results file(s) as output.
+
+This subcommand only filters measurements in `data/[source]/tgen/transfers`
+and `data/[source]/tgen/streams`, but leaves any summaries unchanged.
 """
 HELP_FILTER = """
 Filter OnionPerf analysis results
@@ -295,7 +298,8 @@ files generated by this script will be written""",
 filter_parser.set_defaults(func=filter, formatter_class=my_formatter_class)
 
 filter_parser.add_argument('-i', '--input',
-help="""read the OnionPerf analysis results at PATH as input""",
+help="""a file or directory PATH from which OnionPerf analysis results
+files are read""",
 metavar="PATH", required="True",
 action="store", dest="input")
 
@@ -314,8 +318,8 @@ files generated by this script will be written""",
 default=None)
 
 filter_parser.add_argument('-o', '--output',
-help="""write the filtered output OnionPerf analysis results file to
-PATH""",
+help="""a file or directory PATH where filtered output OnionPerf
+analysis results files are written""",
 metavar="PATH", required="True",
 action="store", dest="output")
 
@@ -439,17 +443,26 @@ def analyze(args):
 def filter(args):
 from onionperf.filtering import Filtering
 
-p = os.path.abspath(os.path.expanduser(args.input))
-if not os.path.exists(p):
-raise argparse.ArgumentTypeError("path '%s' does not exist" % 
args.input)
+input_path = os.path.abspath(os.path.expanduser(args.input))
+if not os.path.exists(input_path):
+raise argparse.ArgumentTypeError("input path '%s' does not exist" % 
args.input)
+output_path = os.path.abspath(os.path.expanduser(args.output))
+if os.path.exists(output_path)

[tor-commits] [onionperf/master] Bump version to 0.7.

2020-09-01 Thread karsten
commit c8275b25e4afda9328634ec6be56ff46c7ee1cfe
Author: Karsten Loesing 
Date:   Tue Sep 1 22:30:12 2020 +0200

Bump version to 0.7.
---
 CHANGELOG.md | 2 +-
 setup.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e0253c5..80fb14d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-# Changes in version 0.7 - 2020-??-??
+# Changes in version 0.7 - 2020-09-01
 
  - Add `onionperf measure --drop-guards` parameter to use and drop
guards and circuit build timeouts every given number of hours, if
diff --git a/setup.py b/setup.py
index 7fd7f85..d72b27a 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ with open('requirements.txt') as f:
 install_requires = f.readlines()
 
 setup(name='OnionPerf',
-  version='0.6',
+  version='0.7',
   description='A utility to monitor, measure, analyze, and visualize the 
performance of Tor and Onion Services',
   author='Rob Jansen',
   url='https://github.com/robgjansen/onionperf/',

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Let TGen client finish by itself in one-shot mode.

2020-09-01 Thread karsten
commit 959cf3689106189001a83c7e58dc40e10497a081
Author: Philipp Winter 
Date:   Fri Aug 7 14:48:58 2020 -0700

Let TGen client finish by itself in one-shot mode.

We tell TGen client to finish on its own by passing the count option to
the end node:
https://github.com/shadow/tgen/blob/master/doc/TGen-Options.md#end-options

This patch adds another argument to the function watchdog_thread_task(),
no_relaunch, which instructs the function to not re-launch its process
if it fails.
---
 onionperf/measurement.py | 45 +++--
 onionperf/model.py   |  3 ++-
 2 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/onionperf/measurement.py b/onionperf/measurement.py
index e2d8d1c..d699292 100644
--- a/onionperf/measurement.py
+++ b/onionperf/measurement.py
@@ -50,10 +50,11 @@ def readline_thread_task(instream, q):
 # wait for lines from stdout until the EOF
 for line in iter(instream.readline, b''): q.put(line)
 
-def watchdog_thread_task(cmd, cwd, writable, done_ev, send_stdin, 
ready_search_str, ready_ev):
+def watchdog_thread_task(cmd, cwd, writable, done_ev, send_stdin, 
ready_search_str, ready_ev, no_relaunch):
 
-# launch or re-launch our sub process until we are told to stop
-# if we fail too many times in too short of time, give up and exit
+# launch or re-launch (or don't re-launch, if no_relaunch is set) our sub
+# process until we are told to stop if we fail too many times in too short
+# of time, give up and exit
 failure_times = []
 pause_time_seconds = 0
 while done_ev.is_set() is False:
@@ -105,6 +106,10 @@ def watchdog_thread_task(cmd, cwd, writable, done_ev, 
send_stdin, ready_search_s
 subp.wait()
 elif done_ev.is_set():
 logging.info("command '{}' finished as expected".format(cmd))
+elif no_relaunch:
+logging.info("command '{}' finished on its own".format(cmd))
+# our command finished on its own. time to terminate.
+done_ev.set()
 else:
 logging.warning("command '{}' finished before 
expected".format(cmd))
 now = time.time()
@@ -284,15 +289,9 @@ class Measurement(object):
 time.sleep(1)
 while True:
 if tgen_model.num_transfers:
-downloads = 0
-while True:
-downloads = 
self.__get_download_count(tgen_client_writable.filename)
-time.sleep(1)
-if downloads >= tgen_model.num_transfers:
-logging.info("Onionperf has downloaded %d 
files and will now shut down." % tgen_model.num_transfers)
-break
-else:
-continue
+# This function blocks until our TGen client process
+# terminated on its own.
+self.__wait_for_tgen_client()
 break
 
 if self.__is_alive():
@@ -366,7 +365,10 @@ class Measurement(object):
 logging.info("Logging TGen {1} process output to 
{0}".format(tgen_logpath, name))
 
 tgen_cmd = "{0} {1}".format(self.tgen_bin_path, tgen_confpath)
-tgen_args = (tgen_cmd, tgen_datadir, tgen_writable, self.done_event, 
None, None, None)
+# If we're running in "one-shot mode", TGen client will terminate on
+# its own and we don't need our watchdog to restart the process.
+no_relaunch = (name == "client" and tgen_model_conf.num_transfers)
+tgen_args = (tgen_cmd, tgen_datadir, tgen_writable, self.done_event, 
None, None, None, no_relaunch)
 tgen_watchdog = threading.Thread(target=watchdog_thread_task, 
name="tgen_{0}_watchdog".format(name), args=tgen_args)
 tgen_watchdog.start()
 self.threads.append(tgen_watchdog)
@@ -464,7 +466,7 @@ WarnUnsafeSocks 0\nSafeLogging 0\nMaxCircuitDirtiness 60 
seconds\nDataDirectory
 tor_stdin_bytes = str_tools._to_bytes(tor_config)
 tor_ready_str = "Bootstrapped 100"
 tor_ready_ev = threading.Event()
-tor_args = (tor_cmd, tor_datadir, tor_writable, self.done_event, 
tor_stdin_bytes, tor_ready_str, tor_ready_ev)
+tor_args = (tor_cmd, tor_datadir, tor_writable, self.done_event, 
tor_stdin_bytes, tor_ready_str, tor_ready_ev, False)
 tor_watchdog = threading.Thread(target=watchdog_thread_task, 
name="tor_{0}_watchdog".format(name), args=tor_args)
 tor_watchdog.start()
 self.threads.append(tor_watchdog)
@@ -491,14 +493,13 @@ WarnUnsafeSocks 0\nSafeLogging 0\nMaxCircuitDirtiness 60 
seconds\nDataDirectory
 
 return tor_writable, torctl_writable
 
-def __get_download_count(self, tgen_logpath):
-count = 0
-if tgen_logpath is not None and os.path.exists(tgen_logpath):
- 

[tor-commits] [onionperf/master] Make some tweaks to new TGen model.

2020-09-01 Thread karsten
commit b8f1e5c2695c097a7494f7975403664c0c833825
Author: Karsten Loesing 
Date:   Sun Aug 16 22:03:34 2020 +0200

Make some tweaks to new TGen model.

 - Change timeout back to 270 seconds and stallout back to 0 seconds.
 - Change initial pause to 300 seconds to keep default behavior
   unchanged.
 - Change model, so that pause_between starts in parallel to a stream,
   not when the stream is completed. This is the same behavior as
   before.

Also add a change log entry for all changes.
---
 CHANGELOG.md|  7 +++
 onionperf/model.py  | 20 
 onionperf/onionperf |  2 +-
 3 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c4c4f2..ac6897b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+# Changes in version 0.7 - 2020-??-??
+
+ - Remove the `onionperf measure --oneshot` switch and replace it with
+   new switches `--tgen-pause-initial`, `--tgen-pause-between`,
+   `--tgen-transfer-size`, and `--tgen-num-transfers ` to further
+   configure the generated TGen model.
+
 # Changes in version 0.6 - 2020-??-??
 
  - Update to TGen 1.0.0, use TGenTools for parsing TGen log files, and
diff --git a/onionperf/model.py b/onionperf/model.py
index d45763e..fde587f 100644
--- a/onionperf/model.py
+++ b/onionperf/model.py
@@ -43,8 +43,8 @@ class TGenLoadableModel(TGenModel):
 
 class TGenModelConf(object):
 """Represents a TGen traffic model configuration."""
-def __init__(self, pause_initial=0, num_transfers=1, transfer_size="5 MiB",
- continuous_transfers=False, pause_between=5, port=None, 
servers=[],
+def __init__(self, pause_initial=300, num_transfers=1, transfer_size="5 
MiB",
+ continuous_transfers=False, pause_between=300, port=None, 
servers=[],
  socks_port=None):
 self.pause_initial = pause_initial
 self.pause_between = pause_between
@@ -103,28 +103,24 @@ class TorperfModel(GeneratableTGenModel):
 g.add_node("stream",
sendsize="0",
recvsize=self.config.transfer_size,
-   timeout="15 seconds",
-   stallout="10 seconds")
+   timeout="270 seconds",
+   stallout="0 seconds")
 g.add_node("pause_between",
time="%d seconds" % self.config.pause_between)
 
 g.add_edge("start", "pause_initial")
 g.add_edge("pause_initial", "stream")
+g.add_edge("pause_initial", "pause_between")
+g.add_edge("pause_between", "stream")
+g.add_edge("pause_between", "pause_between")
 
 # only add an end node if we need to stop
-if self.config.continuous_transfers:
-# continuous mode, i.e., no end node
-g.add_edge("stream", "pause_between")
-else:
+if not self.config.continuous_transfers:
 # one-shot mode, i.e., end after configured number of transfers
 g.add_node("end",
count="%d" % self.config.num_transfers)
 # check for end condition after every transfer
 g.add_edge("stream", "end")
-# if end condition not met, pause
-g.add_edge("end", "pause_between")
-
-g.add_edge("pause_between", "stream")
 
 return g
 
diff --git a/onionperf/onionperf b/onionperf/onionperf
index a49982b..6a16da2 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -194,7 +194,7 @@ def main():
 help="""the number of seconds TGen should wait before walking through 
its action graph""",
 metavar="N", type=int,
 action="store", dest="tgenpauseinitial",
-default=5)
+default=300)
 
 measure_parser.add_argument('--tgen-pause-between',
 help="""the number of seconds TGen should wait in between two 
transfers""",



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Merge remote-tracking branch 'karsten/task-33399-2' into develop

2020-09-01 Thread karsten
commit 8fe84702dea4b2e4640c0817e505e6c449e7b9a8
Merge: f496ede 9d76ca4
Author: Karsten Loesing 
Date:   Tue Sep 1 22:28:52 2020 +0200

Merge remote-tracking branch 'karsten/task-33399-2' into develop

 CHANGELOG.md |  3 ++-
 onionperf/monitor.py | 12 +++-
 onionperf/onionperf  |  2 +-
 3 files changed, 10 insertions(+), 7 deletions(-)



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Tweak #33399 patch.

2020-09-01 Thread karsten
commit dfec0b8960ac214f63985dfc317ffce750ef5922
Author: Karsten Loesing 
Date:   Thu Aug 20 15:40:25 2020 +0200

Tweak #33399 patch.

 - Add a change log entry.
 - Pick are more sensible default for `drop_guards_interval_hours`,
   also to fix unit tests.
---
 CHANGELOG.md | 5 +
 onionperf/measurement.py | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b8a86ee..c31a40e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# Changes in version 0.7 - 2020-??-??
+
+ - Add `onionperf measure --drop-guards` parameter to use and drop
+   guards after a given number of hours. Implements #33399.
+
 # Changes in version 0.6 - 2020-08-08
 
  - Update to TGen 1.0.0, use TGenTools for parsing TGen log files, and
diff --git a/onionperf/measurement.py b/onionperf/measurement.py
index 1a5f3bb..709fbc6 100644
--- a/onionperf/measurement.py
+++ b/onionperf/measurement.py
@@ -173,7 +173,7 @@ def logrotate_thread_task(writables, tgen_writable, 
torctl_writable, docroot, ni
 
 class Measurement(object):
 
-def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, oneshot, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False, 
drop_guards_interval_hours=None):
+def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, oneshot, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False, 
drop_guards_interval_hours=0):
 self.tor_bin_path = tor_bin_path
 self.tgen_bin_path = tgen_bin_path
 self.datadir_path = datadir_path



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Tweak --drop-guards switch.

2020-09-01 Thread karsten
commit 9d76ca4b3847a10c9565d12cf130933a1076297b
Author: Karsten Loesing 
Date:   Tue Sep 1 17:02:47 2020 +0200

Tweak --drop-guards switch.

This commit tweaks the recently added --drop-guards switch as follows:

 - Guards are dropped right at startup and then every N hours.
   Otherwise we might not receive the first round of GUARD NEW/UP
   events. It's unclear why we don't receive those events, but finding
   out might be time-consuming whereas dropping guards at startup is
   basically free.

 - Right after guards are dropped, circuit build timeouts are dropped,
   too, if supported by the Tor version. If the Tor version does not
   support this, there's going to be a warning, and the control log
   will simply not contain BUILDTIMEOUT_SET events.

Still part of the reopened tpo/metrics/onionperf#33399.
---
 CHANGELOG.md |  3 ++-
 onionperf/monitor.py | 12 +++-
 onionperf/onionperf  |  2 +-
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a212842..e0253c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,8 @@
 # Changes in version 0.7 - 2020-??-??
 
  - Add `onionperf measure --drop-guards` parameter to use and drop
-   guards after a given number of hours. Implements #33399.
+   guards and circuit build timeouts every given number of hours, if
+   supported by the Tor version. Implements #33399.
  - Remove the `onionperf measure --oneshot` switch and replace it with
new switches `--tgen-pause-initial`, `--tgen-pause-between`,
`--tgen-transfer-size`, and `--tgen-num-transfers ` to further
diff --git a/onionperf/monitor.py b/onionperf/monitor.py
index b5e6390..886a9a0 100644
--- a/onionperf/monitor.py
+++ b/onionperf/monitor.py
@@ -57,21 +57,23 @@ class TorMonitor(object):
 interval_count = 0
 if newnym_interval_seconds is not None:
 next_newnym = newnym_interval_seconds
-if drop_guards_interval_hours > 0:
-next_drop_guards = drop_guards_interval_hours * 3600
+next_drop_guards = 0
 while done_ev is None or not done_ev.is_set():
 # if self.filepath != '-' and 
os.path.exists(self.filepath):
 #with open(self.filepath, 'rb') as sizef:
 #msg = "tor-ctl-logger[port={0}] logged {1} bytes 
to {2}, press CTRL-C to quit".format(self.tor_ctl_port, 
os.fstat(sizef.fileno()).st_size, self.filepath)
 #logging.info(msg)
+if drop_guards_interval_hours > 0 and interval_count >= 
next_drop_guards:
+next_drop_guards += drop_guards_interval_hours * 3600
+torctl.drop_guards()
+drop_timeouts_response = torctl.msg("DROPTIMEOUTS")
+if not drop_timeouts_response.is_ok():
+self.__log(self.writable, "[WARNING] unrecognized 
command DROPTIMEOUTS in tor\n")
 sleep(1)
 interval_count += 1
 if newnym_interval_seconds is not None and interval_count 
>= next_newnym:
 next_newnym += newnym_interval_seconds
 torctl.signal(Signal.NEWNYM)
-if drop_guards_interval_hours > 0 and interval_count >= 
next_drop_guards:
-next_drop_guards += drop_guards_interval_hours * 3600
-torctl.drop_guards()
 
 except KeyboardInterrupt:
 pass  # the user hit ctrl+c
diff --git a/onionperf/onionperf b/onionperf/onionperf
index e6aa44a..d3d8e31 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -215,7 +215,7 @@ def main():
 default=0)
 
 measure_parser.add_argument('--drop-guards',
-help="""Use and drop guards every N > 0 hours, or do not use guards at 
all if N = 0""",
+help="""Use and drop guards and circuit build timeouts every N > 0 
hours, or do not use guards at all and never drop circuit build timeouts if N = 
0""",
 metavar="N", type=type_nonnegative_integer,
 action="store", dest="drop_guards_interval_hours",
 default=0)



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Apply 1 suggestion(s) to 1 file(s)

2020-09-01 Thread karsten
commit a6aa4189e04ee1b05dbdb68b90f24e14bf3443ac
Author: Philipp Winter 
Date:   Fri Aug 14 16:47:04 2020 +

Apply 1 suggestion(s) to 1 file(s)
---
 onionperf/model.py | 54 ++
 1 file changed, 22 insertions(+), 32 deletions(-)

diff --git a/onionperf/model.py b/onionperf/model.py
index bdd5a53..b589249 100644
--- a/onionperf/model.py
+++ b/onionperf/model.py
@@ -104,40 +104,30 @@ class TorperfModel(GeneratableTGenModel):
 # "One-shot mode," i.e., onionperf will stop after the given number of
 # iterations.  The idea is:
 # start -> pause -> stream-1 -> pause-1 -> ... -> stream-n -> pause-n 
-> end
-if self.config.num_transfers > 0:
-for i in range(self.config.num_transfers):
-g.add_node("stream-%d" % i,
-   sendsize="0",
-   recvsize=self.config.transfer_size,
-   timeout="15 seconds",
-   stallout="10 seconds")
-g.add_node("pause-%d" % i,
-   time="%d seconds" % 
self.config.inter_transfer_pause)
-
-g.add_edge("stream-%d" % i, "pause-%d" % i)
-if i > 0:
-g.add_edge("pause-%d" % (i-1), "stream-%d" % i)
-
+g.add_node("stream",
+   sendsize="0",
+   recvsize=self.config.transfer_size,
+   timeout="15 seconds",
+   stallout="10 seconds")
+g.add_node("pause_between",
+   time="%d seconds" % self.config.inter_transfer_pause)
+
+g.add_edge("pause_initial", "stream")
+
+# only add an end node if we need to stop
+if self.config.continuous_transfers:
+# continuous mode, i.e., no end node
+g.add_edge("stream", "pause_between")
+else:
+# one-shot mode, i.e., end after configured number of transfers
 g.add_node("end",
count=str(self.config.num_transfers))
-g.add_edge("pause", "stream-0")
-g.add_edge("pause-%d" % (self.config.num_transfers - 1), "end")
-
-# Continuous mode, i.e., onionperf will not stop.  The idea is:
-# start -> pause -> stream -> pause
-#   ^   |
-#   +---+
-elif self.config.continuous_transfers:
-g.add_node("stream",
-   sendsize="0",
-   recvsize=self.config.transfer_size,
-   timeout="15 seconds",
-   stallout="10 seconds")
-g.add_node("pause",
-   time="%d seconds" % self.config.inter_transfer_pause)
-g.add_edge("pause", "stream")
-g.add_edge("stream", "pause")
-g.add_edge("pause", "stream")
+# check for end condition after every transfer
+g.add_edge("stream", "end")
+# if end condition not met, pause
+g.add_edge("end", "pause_between")
+
+g.add_edge("pause_between", "stream")
 
 return g
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Measure static guard nodes.

2020-09-01 Thread karsten
commit e9fd47d95db102b3a7ace36fa412e18d182c5fa4
Author: Karsten Loesing 
Date:   Tue Jun 16 21:30:07 2020 +0200

Measure static guard nodes.

Add --drop-guards parameter to use and drop guards after a given
number of hours.

Implements #33399.
---
 onionperf/measurement.py |  7 ---
 onionperf/monitor.py | 18 +-
 onionperf/onionperf  |  9 -
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/onionperf/measurement.py b/onionperf/measurement.py
index 4a58bc4..899b277 100644
--- a/onionperf/measurement.py
+++ b/onionperf/measurement.py
@@ -172,7 +172,7 @@ def logrotate_thread_task(writables, tgen_writable, 
torctl_writable, docroot, ni
 
 class Measurement(object):
 
-def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, oneshot, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False):
+def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, oneshot, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False, 
drop_guards_interval_hours=None):
 self.tor_bin_path = tor_bin_path
 self.tgen_bin_path = tgen_bin_path
 self.datadir_path = datadir_path
@@ -188,6 +188,7 @@ class Measurement(object):
 self.torclient_conf_file = torclient_conf_file
 self.torserver_conf_file = torserver_conf_file
 self.single_onion = single_onion
+self.drop_guards_interval_hours = drop_guards_interval_hours
 
 def run(self, do_onion=True, do_inet=True, client_tgen_listen_port=5, 
client_tgen_connect_ip='0.0.0.0', client_tgen_connect_port=8080, 
client_tor_ctl_port=59050, client_tor_socks_port=59000,
  server_tgen_listen_port=8080, server_tor_ctl_port=59051, 
server_tor_socks_port=59001):
@@ -388,7 +389,7 @@ WarnUnsafeSocks 0\nSafeLogging 0\nMaxCircuitDirtiness 60 
seconds\nDataDirectory
 tor_config = tor_config + f.read()
 if name == "client" and self.additional_client_conf:
 tor_config += self.additional_client_conf
-if not 'UseEntryGuards' in tor_config and not 'UseBridges' in 
tor_config:
+if not 'UseEntryGuards' in tor_config and not 'UseBridges' in 
tor_config and self.drop_guards_interval_hours == 0:
 tor_config += "UseEntryGuards 0\n"
 if name == "server" and self.single_onion:
 tor_config += "HiddenServiceSingleHopMode 
1\nHiddenServiceNonAnonymousMode 1\n"
@@ -467,7 +468,7 @@ WarnUnsafeSocks 0\nSafeLogging 0\nMaxCircuitDirtiness 60 
seconds\nDataDirectory
 
 torctl_events = [e for e in monitor.get_supported_torctl_events() if e 
not in ['DEBUG', 'INFO', 'NOTICE', 'WARN', 'ERR']]
 newnym_interval_seconds = 300
-torctl_args = (control_port, torctl_writable, torctl_events, 
newnym_interval_seconds, self.done_event)
+torctl_args = (control_port, torctl_writable, torctl_events, 
newnym_interval_seconds, self.drop_guards_interval_hours, self.done_event)
 torctl_helper = threading.Thread(target=monitor.tor_monitor_run, 
name="torctl_{0}_helper".format(name), args=torctl_args)
 torctl_helper.start()
 self.threads.append(torctl_helper)
diff --git a/onionperf/monitor.py b/onionperf/monitor.py
index 5387bff..ac6fea9 100644
--- a/onionperf/monitor.py
+++ b/onionperf/monitor.py
@@ -22,7 +22,7 @@ class TorMonitor(object):
 self.writable = writable
 self.events = events
 
-def run(self, newnym_interval_seconds=None, done_ev=None):
+def run(self, newnym_interval_seconds=None, drop_guards_interval_hours=0, 
done_ev=None):
 with Controller.from_port(port=self.tor_ctl_port) as torctl:
 torctl.authenticate()
 
@@ -54,6 +54,10 @@ class TorMonitor(object):
 # let stem run its threads and log all of the events, until user 
interrupts
 try:
 interval_count = 0
+if newnym_interval_seconds is not None:
+next_newnym = newnym_interval_seconds
+if drop_guards_interval_hours > 0:
+next_drop_guards = drop_guards_interval_hours * 3600
 while done_ev is None or not done_ev.is_set():
 # if self.filepath != '-' and 
os.path.exists(self.filepath):
 #with open(self.filepath, 'rb') as sizef:
@@ -61,9 +65,13 @@ class TorMonitor(object):
 #logging.info(msg)
 sleep(1)
 interval_count += 1
-if newnym_interval_seconds is not None and interval_count 
>= newnym_interval_seconds:
-interval_count = 0
+if newnym_interval_seconds is not None and interval_count 
>= next_newnym:
+next_newnym += new

[tor-commits] [onionperf/master] Merge branch 'phw-enhancement-33432-3' into develop

2020-09-01 Thread karsten
commit c707674ba9b0d7931038c12bbe2d01585a88eb22
Merge: dfec0b8 b8f1e5c
Author: Karsten Loesing 
Date:   Thu Aug 27 10:38:21 2020 +0200

Merge branch 'phw-enhancement-33432-3' into develop

 CHANGELOG.md|   4 ++
 onionperf/measurement.py| 139 +++-
 onionperf/model.py  |  98 ++---
 onionperf/onionperf |  60 
 onionperf/tests/test_measurement.py |  12 ++--
 5 files changed, 185 insertions(+), 128 deletions(-)

diff --cc CHANGELOG.md
index c31a40e,ac6897b..4b7fcb2
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@@ -1,9 -1,11 +1,13 @@@
  # Changes in version 0.7 - 2020-??-??
  
 + - Add `onionperf measure --drop-guards` parameter to use and drop
 +   guards after a given number of hours. Implements #33399.
+  - Remove the `onionperf measure --oneshot` switch and replace it with
+new switches `--tgen-pause-initial`, `--tgen-pause-between`,
+`--tgen-transfer-size`, and `--tgen-num-transfers ` to further
+configure the generated TGen model.
  
 -# Changes in version 0.6 - 2020-??-??
 +# Changes in version 0.6 - 2020-08-08
  
   - Update to TGen 1.0.0, use TGenTools for parsing TGen log files, and
 update analysis results file version to 3.0. Implements #33974.
diff --cc onionperf/measurement.py
index 709fbc6,d699292..f198be0
--- a/onionperf/measurement.py
+++ b/onionperf/measurement.py
@@@ -173,7 -188,7 +188,7 @@@ def logrotate_thread_task(writables, tg
  
  class Measurement(object):
  
- def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, oneshot, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False, 
drop_guards_interval_hours=0):
 -def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False):
++def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False, 
drop_guards_interval_hours=0):
  self.tor_bin_path = tor_bin_path
  self.tgen_bin_path = tgen_bin_path
  self.datadir_path = datadir_path
@@@ -189,13 -203,11 +203,12 @@@
  self.torclient_conf_file = torclient_conf_file
  self.torserver_conf_file = torserver_conf_file
  self.single_onion = single_onion
 +self.drop_guards_interval_hours = drop_guards_interval_hours
  
- def run(self, do_onion=True, do_inet=True, client_tgen_listen_port=5, 
client_tgen_connect_ip='0.0.0.0', client_tgen_connect_port=8080, 
client_tor_ctl_port=59050, client_tor_socks_port=59000,
-  server_tgen_listen_port=8080, server_tor_ctl_port=59051, 
server_tor_socks_port=59001):
+ def run(self, do_onion=True, do_inet=True, tgen_model=None, 
tgen_client_conf=None, tgen_server_conf=None):
  '''
- only `server_tgen_listen_port` are "public" and need to be opened on 
the firewall.
- if `client_tgen_connect_port` != `server_tgen_listen_port`, then you 
should have installed a forwarding rule in the firewall.
+ only `tgen_server_conf.listen_port` are "public" and need to be 
opened on the firewall.
+ if `tgen_client_conf.connect_port` != `tgen_server_conf.listen_port`, 
then you should have installed a forwarding rule in the firewall.
  all ports need to be unique though, and unique among multiple 
onionperf instances.
  
  here are some sane defaults:
diff --cc onionperf/onionperf
index 641db70,6a16da2..e6aa44a
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@@ -195,12 -190,30 +190,36 @@@ def main()
  action="store", dest="tgenconnectport",
  default=8080)
  
+ measure_parser.add_argument('--tgen-pause-initial',
+ help="""the number of seconds TGen should wait before walking through 
its action graph""",
+ metavar="N", type=int,
+ action="store", dest="tgenpauseinitial",
+ default=300)
+ 
+ measure_parser.add_argument('--tgen-pause-between',
+ help="""the number of seconds TGen should wait in between two 
transfers""",
+ metavar="N", type=int,
+ action="store", dest="tgenpausebetween",
+ default=300)
+ 
+ measure_parser.add_argument('--tgen-transfer-size',
+ help="""the size of the file transfer that TGen will perform (e.g., 
'5 MiB' or '10 KiB')""",
+ metavar="STRING", type=str,
+ action="store", dest="tgentransfersize",
+ default="5 MiB")
+ 
+ measure_parser.add_argument('--tgen-num-transfers',

[tor-commits] [onionperf/master] Rename variables for consistency and clarity.

2020-09-01 Thread karsten
commit 86f746c3c6ee6dc3be342f53fea90e6c02d39991
Author: Philipp Winter 
Date:   Fri Aug 14 10:49:09 2020 -0700

Rename variables for consistency and clarity.

initial_pause-> pause_initial
inter_transfer_pause -> pause_between

Thanks to Rob for the suggestion.
---
 onionperf/model.py  | 19 ---
 onionperf/onionperf | 12 ++--
 2 files changed, 14 insertions(+), 17 deletions(-)

diff --git a/onionperf/model.py b/onionperf/model.py
index b589249..3bfe35f 100644
--- a/onionperf/model.py
+++ b/onionperf/model.py
@@ -43,14 +43,14 @@ class TGenLoadableModel(TGenModel):
 
 class TGenModelConf(object):
 """Represents a TGen traffic model configuration."""
-def __init__(self, initial_pause=0, num_transfers=1, transfer_size="5 MiB",
- continuous_transfers=False, inter_transfer_pause=5, 
port=None, servers=[],
+def __init__(self, pause_initial=0, num_transfers=1, transfer_size="5 MiB",
+ continuous_transfers=False, pause_between=5, port=None, 
servers=[],
  socks_port=None):
-self.initial_pause = initial_pause
+self.pause_initial = pause_initial
+self.pause_between = pause_between
 self.num_transfers = num_transfers
 self.transfer_size = transfer_size
 self.continuous_transfers = continuous_transfers
-self.inter_transfer_pause = inter_transfer_pause
 self.port = port
 self.servers = servers
 self.socks_port = socks_port
@@ -98,20 +98,17 @@ class TorperfModel(GeneratableTGenModel):
loglevel="info",
heartbeat="1 minute")
 
-g.add_node("pause", time="%d seconds" % self.config.initial_pause)
-g.add_edge("start", "pause")
-
-# "One-shot mode," i.e., onionperf will stop after the given number of
-# iterations.  The idea is:
-# start -> pause -> stream-1 -> pause-1 -> ... -> stream-n -> pause-n 
-> end
+g.add_node("pause_initial",
+   time="%d seconds" % self.config.pause_initial)
 g.add_node("stream",
sendsize="0",
recvsize=self.config.transfer_size,
timeout="15 seconds",
stallout="10 seconds")
 g.add_node("pause_between",
-   time="%d seconds" % self.config.inter_transfer_pause)
+   time="%d seconds" % self.config.pause_between)
 
+g.add_edge("start", "pause_initial")
 g.add_edge("pause_initial", "stream")
 
 # only add an end node if we need to stop
diff --git a/onionperf/onionperf b/onionperf/onionperf
index d95e691..a49982b 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -190,16 +190,16 @@ def main():
 action="store", dest="tgenconnectport",
 default=8080)
 
-measure_parser.add_argument('--tgen-start-pause',
+measure_parser.add_argument('--tgen-pause-initial',
 help="""the number of seconds TGen should wait before walking through 
its action graph""",
 metavar="N", type=int,
-action="store", dest="tgenstartpause",
+action="store", dest="tgenpauseinitial",
 default=5)
 
-measure_parser.add_argument('--tgen-intertransfer-pause',
+measure_parser.add_argument('--tgen-pause-between',
 help="""the number of seconds TGen should wait in between two 
transfers""",
 metavar="N", type=int,
-action="store", dest="tgenintertransferpause",
+action="store", dest="tgenpausebetween",
 default=300)
 
 measure_parser.add_argument('--tgen-transfer-size',
@@ -377,11 +377,11 @@ def measure(args):
 tor_ctl_port=server_tor_ctl_port,
 tor_socks_port=server_tor_socks_port)
 
-tgen_model = TGenModelConf(initial_pause=args.tgenstartpause,
+tgen_model = TGenModelConf(pause_initial=args.tgenpauseinitial,
transfer_size=args.tgentransfersize,
num_transfers=args.tgennumtransfers,
continuous_transfers=args.tgennumtransfers 
== 0,
-   
inter_transfer_pause=args.tgenintertransferpause)
+   pause_between=args.tgenpausebetween)
 
 meas = Measurement(args.torpath,
args.tgenpath,



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Add issue number to change log.

2020-09-01 Thread karsten
commit f496edec6fec8adc0b4e5c0454989c2bc06312e3
Author: Karsten Loesing 
Date:   Thu Aug 27 11:09:07 2020 +0200

Add issue number to change log.
---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b7fcb2..a212842 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@
  - Remove the `onionperf measure --oneshot` switch and replace it with
new switches `--tgen-pause-initial`, `--tgen-pause-between`,
`--tgen-transfer-size`, and `--tgen-num-transfers ` to further
-   configure the generated TGen model.
+   configure the generated TGen model. Implemets #33432.
 
 # Changes in version 0.6 - 2020-08-08
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Merge branch 'task-33399' into develop

2020-09-01 Thread karsten
commit 4ff257c4270c0d1e5fd0f1ef76e640696ec2c514
Merge: e333be2 e9fd47d
Author: Karsten Loesing 
Date:   Thu Aug 20 15:05:23 2020 +0200

Merge branch 'task-33399' into develop

 onionperf/measurement.py |  7 ---
 onionperf/monitor.py | 18 +-
 onionperf/onionperf  |  9 -
 3 files changed, 25 insertions(+), 9 deletions(-)




___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Use format string for consistency.

2020-09-01 Thread karsten
commit 1eea5e10700c76f8e1b37e626eaeaf96c5488150
Author: Philipp Winter 
Date:   Fri Aug 14 11:29:01 2020 -0700

Use format string for consistency.

Thanks to Rob for pointing this out.
---
 onionperf/model.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/onionperf/model.py b/onionperf/model.py
index 3bfe35f..d45763e 100644
--- a/onionperf/model.py
+++ b/onionperf/model.py
@@ -118,7 +118,7 @@ class TorperfModel(GeneratableTGenModel):
 else:
 # one-shot mode, i.e., end after configured number of transfers
 g.add_node("end",
-   count=str(self.config.num_transfers))
+   count="%d" % self.config.num_transfers)
 # check for end condition after every transfer
 g.add_edge("stream", "end")
 # if end condition not met, pause



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/master] Make models more configurable.

2020-09-01 Thread karsten
commit 7ef8c64833d41337d5c9cc5baaee2808092c9aad
Author: Philipp Winter 
Date:   Fri Jun 26 10:00:29 2020 -0700

Make models more configurable.

This patch removes the --oneshot subcommand and replaces it with several
new subcommands for OnionPerf's "measure" command:

--tgen-start-pause  (Initial pause before file transfers.)
--tgen-num-transfers(Number of file transfers.)
--tgen-intertransfer-pause  (Pause in between file transfers.)
--tgen-transfer-size(Size of each file transfer.)

By default, OnionPerf continues to run in "continuous" mode.  One can
simulate oneshot mode by running onionperf with the following flags:

onionperf measure --tgen-num-transfers=1

In addition to the above subcommands, this patch improves the code base
by 1) adding a TGenConf class to hold TGen's configuration and by 2)
adding a TGenModelConf class to hold TGen's traffic model.

This fixes tpo/metrics/onionperf#33432.
---
 onionperf/measurement.py| 102 +++---
 onionperf/model.py  | 108 +++-
 onionperf/onionperf |  60 +++-
 onionperf/tests/test_measurement.py |  12 ++--
 4 files changed, 175 insertions(+), 107 deletions(-)

diff --git a/onionperf/measurement.py b/onionperf/measurement.py
index af1fa0d..e2d8d1c 100644
--- a/onionperf/measurement.py
+++ b/onionperf/measurement.py
@@ -15,6 +15,16 @@ from stem.control import Controller
 from stem.version import Version, Requirement, get_system_tor_version
 from stem import __version__ as stem_version
 
+class TGenConf(object):
+"""Represents a TGen configuration, for both client and server."""
+def __init__(self, listen_port=None, connect_ip=None, connect_port=None, 
tor_ctl_port=None, tor_socks_port=None):
+self.listen_port = str(listen_port)
+self.tor_ctl_port = tor_ctl_port
+self.tor_socks_port = tor_socks_port
+# TGen clients use connect_ip and connect_port.
+self.connect_ip = connect_ip
+self.connect_port = connect_port
+
 # onionperf imports
 from . import analysis, monitor, model, util
 
@@ -173,12 +183,11 @@ def logrotate_thread_task(writables, tgen_writable, 
torctl_writable, docroot, ni
 
 class Measurement(object):
 
-def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, oneshot, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False):
+def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False):
 self.tor_bin_path = tor_bin_path
 self.tgen_bin_path = tgen_bin_path
 self.datadir_path = datadir_path
 self.privatedir_path = privatedir_path
-self.oneshot = oneshot
 self.nickname = nickname
 self.threads = None
 self.done_event = None
@@ -190,20 +199,30 @@ class Measurement(object):
 self.torserver_conf_file = torserver_conf_file
 self.single_onion = single_onion
 
-def run(self, do_onion=True, do_inet=True, client_tgen_listen_port=5, 
client_tgen_connect_ip='0.0.0.0', client_tgen_connect_port=8080, 
client_tor_ctl_port=59050, client_tor_socks_port=59000,
- server_tgen_listen_port=8080, server_tor_ctl_port=59051, 
server_tor_socks_port=59001):
+def run(self, do_onion=True, do_inet=True, tgen_model=None, 
tgen_client_conf=None, tgen_server_conf=None):
 '''
-only `server_tgen_listen_port` are "public" and need to be opened on 
the firewall.
-if `client_tgen_connect_port` != `server_tgen_listen_port`, then you 
should have installed a forwarding rule in the firewall.
+only `tgen_server_conf.listen_port` are "public" and need to be opened 
on the firewall.
+if `tgen_client_conf.connect_port` != `tgen_server_conf.listen_port`, 
then you should have installed a forwarding rule in the firewall.
 all ports need to be unique though, and unique among multiple 
onionperf instances.
 
 here are some sane defaults:
-client_tgen_listen_port=5, client_tgen_connect_port=8080, 
client_tor_ctl_port=59050, client_tor_socks_port=59000,
-server_tgen_listen_port=8080, server_tor_ctl_port=59051, 
server_tor_socks_port=59001
+tgen_client_conf.listen_port=5, 
tgen_client_conf.connect_port=8080, tgen_client_conf.tor_ctl_port=59050, 
tgen_client_conf.tor_socks_port=59000,
+tgen_server_conf.listen_port=8080, 
tgen_server_conf.tor_ctl_port=59051, tgen_server_conf.tor_socks_port=59001
 '''
 self.threads = []
 self.done_event = threading.Event()
 
+if tgen_client_conf is None:
+tgen_client_conf = TGenConf(listen_port=5,
+   

[tor-commits] [onionperf/develop] Tweak --drop-guards switch.

2020-09-01 Thread karsten
commit 9d76ca4b3847a10c9565d12cf130933a1076297b
Author: Karsten Loesing 
Date:   Tue Sep 1 17:02:47 2020 +0200

Tweak --drop-guards switch.

This commit tweaks the recently added --drop-guards switch as follows:

 - Guards are dropped right at startup and then every N hours.
   Otherwise we might not receive the first round of GUARD NEW/UP
   events. It's unclear why we don't receive those events, but finding
   out might be time-consuming whereas dropping guards at startup is
   basically free.

 - Right after guards are dropped, circuit build timeouts are dropped,
   too, if supported by the Tor version. If the Tor version does not
   support this, there's going to be a warning, and the control log
   will simply not contain BUILDTIMEOUT_SET events.

Still part of the reopened tpo/metrics/onionperf#33399.
---
 CHANGELOG.md |  3 ++-
 onionperf/monitor.py | 12 +++-
 onionperf/onionperf  |  2 +-
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a212842..e0253c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,8 @@
 # Changes in version 0.7 - 2020-??-??
 
  - Add `onionperf measure --drop-guards` parameter to use and drop
-   guards after a given number of hours. Implements #33399.
+   guards and circuit build timeouts every given number of hours, if
+   supported by the Tor version. Implements #33399.
  - Remove the `onionperf measure --oneshot` switch and replace it with
new switches `--tgen-pause-initial`, `--tgen-pause-between`,
`--tgen-transfer-size`, and `--tgen-num-transfers ` to further
diff --git a/onionperf/monitor.py b/onionperf/monitor.py
index b5e6390..886a9a0 100644
--- a/onionperf/monitor.py
+++ b/onionperf/monitor.py
@@ -57,21 +57,23 @@ class TorMonitor(object):
 interval_count = 0
 if newnym_interval_seconds is not None:
 next_newnym = newnym_interval_seconds
-if drop_guards_interval_hours > 0:
-next_drop_guards = drop_guards_interval_hours * 3600
+next_drop_guards = 0
 while done_ev is None or not done_ev.is_set():
 # if self.filepath != '-' and 
os.path.exists(self.filepath):
 #with open(self.filepath, 'rb') as sizef:
 #msg = "tor-ctl-logger[port={0}] logged {1} bytes 
to {2}, press CTRL-C to quit".format(self.tor_ctl_port, 
os.fstat(sizef.fileno()).st_size, self.filepath)
 #logging.info(msg)
+if drop_guards_interval_hours > 0 and interval_count >= 
next_drop_guards:
+next_drop_guards += drop_guards_interval_hours * 3600
+torctl.drop_guards()
+drop_timeouts_response = torctl.msg("DROPTIMEOUTS")
+if not drop_timeouts_response.is_ok():
+self.__log(self.writable, "[WARNING] unrecognized 
command DROPTIMEOUTS in tor\n")
 sleep(1)
 interval_count += 1
 if newnym_interval_seconds is not None and interval_count 
>= next_newnym:
 next_newnym += newnym_interval_seconds
 torctl.signal(Signal.NEWNYM)
-if drop_guards_interval_hours > 0 and interval_count >= 
next_drop_guards:
-next_drop_guards += drop_guards_interval_hours * 3600
-torctl.drop_guards()
 
 except KeyboardInterrupt:
 pass  # the user hit ctrl+c
diff --git a/onionperf/onionperf b/onionperf/onionperf
index e6aa44a..d3d8e31 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -215,7 +215,7 @@ def main():
 default=0)
 
 measure_parser.add_argument('--drop-guards',
-help="""Use and drop guards every N > 0 hours, or do not use guards at 
all if N = 0""",
+help="""Use and drop guards and circuit build timeouts every N > 0 
hours, or do not use guards at all and never drop circuit build timeouts if N = 
0""",
 metavar="N", type=type_nonnegative_integer,
 action="store", dest="drop_guards_interval_hours",
 default=0)



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Merge remote-tracking branch 'karsten/task-33399-2' into develop

2020-09-01 Thread karsten
commit 8fe84702dea4b2e4640c0817e505e6c449e7b9a8
Merge: f496ede 9d76ca4
Author: Karsten Loesing 
Date:   Tue Sep 1 22:28:52 2020 +0200

Merge remote-tracking branch 'karsten/task-33399-2' into develop

 CHANGELOG.md |  3 ++-
 onionperf/monitor.py | 12 +++-
 onionperf/onionperf  |  2 +-
 3 files changed, 10 insertions(+), 7 deletions(-)



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Bump version to 0.7.

2020-09-01 Thread karsten
commit c8275b25e4afda9328634ec6be56ff46c7ee1cfe
Author: Karsten Loesing 
Date:   Tue Sep 1 22:30:12 2020 +0200

Bump version to 0.7.
---
 CHANGELOG.md | 2 +-
 setup.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e0253c5..80fb14d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-# Changes in version 0.7 - 2020-??-??
+# Changes in version 0.7 - 2020-09-01
 
  - Add `onionperf measure --drop-guards` parameter to use and drop
guards and circuit build timeouts every given number of hours, if
diff --git a/setup.py b/setup.py
index 7fd7f85..d72b27a 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ with open('requirements.txt') as f:
 install_requires = f.readlines()
 
 setup(name='OnionPerf',
-  version='0.6',
+  version='0.7',
   description='A utility to monitor, measure, analyze, and visualize the 
performance of Tor and Onion Services',
   author='Rob Jansen',
   url='https://github.com/robgjansen/onionperf/',

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [metrics-web/master] Clarify base64 encoding of sha256 digest.

2020-08-28 Thread karsten
commit e58e6e1f2d1da5b60abca31fa9a263e3d3a3f4ec
Author: Karsten Loesing 
Date:   Fri Aug 28 10:56:15 2020 +0200

Clarify base64 encoding of sha256 digest.
---
 src/main/resources/web/jsps/collector.jsp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/resources/web/jsps/collector.jsp 
b/src/main/resources/web/jsps/collector.jsp
index b738089..788128c 100644
--- a/src/main/resources/web/jsps/collector.jsp
+++ b/src/main/resources/web/jsps/collector.jsp
@@ -955,7 +955,8 @@ Index files use the following custom JSON data format that 
might still be extend
 "types": Descriptor types as found in @type 
annotations of contained descriptors.
 "first_published": Earliest publication timestamp of 
contained descriptors using pattern "-MM-DD HH:MM" in the UTC 
timezone.
 "last_published": Latest publication timestamp of contained 
descriptors using pattern "-MM-DD HH:MM" in the UTC 
timezone.
-"sha256": SHA-256 digest of this file.
+"sha256": SHA-256 digest of this file, encoded in Base64.
+
 
 
 

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Add issue number to change log.

2020-08-27 Thread karsten
commit f496edec6fec8adc0b4e5c0454989c2bc06312e3
Author: Karsten Loesing 
Date:   Thu Aug 27 11:09:07 2020 +0200

Add issue number to change log.
---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b7fcb2..a212842 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@
  - Remove the `onionperf measure --oneshot` switch and replace it with
new switches `--tgen-pause-initial`, `--tgen-pause-between`,
`--tgen-transfer-size`, and `--tgen-num-transfers ` to further
-   configure the generated TGen model.
+   configure the generated TGen model. Implemets #33432.
 
 # Changes in version 0.6 - 2020-08-08
 

___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Rename variables for consistency and clarity.

2020-08-27 Thread karsten
commit 86f746c3c6ee6dc3be342f53fea90e6c02d39991
Author: Philipp Winter 
Date:   Fri Aug 14 10:49:09 2020 -0700

Rename variables for consistency and clarity.

initial_pause-> pause_initial
inter_transfer_pause -> pause_between

Thanks to Rob for the suggestion.
---
 onionperf/model.py  | 19 ---
 onionperf/onionperf | 12 ++--
 2 files changed, 14 insertions(+), 17 deletions(-)

diff --git a/onionperf/model.py b/onionperf/model.py
index b589249..3bfe35f 100644
--- a/onionperf/model.py
+++ b/onionperf/model.py
@@ -43,14 +43,14 @@ class TGenLoadableModel(TGenModel):
 
 class TGenModelConf(object):
 """Represents a TGen traffic model configuration."""
-def __init__(self, initial_pause=0, num_transfers=1, transfer_size="5 MiB",
- continuous_transfers=False, inter_transfer_pause=5, 
port=None, servers=[],
+def __init__(self, pause_initial=0, num_transfers=1, transfer_size="5 MiB",
+ continuous_transfers=False, pause_between=5, port=None, 
servers=[],
  socks_port=None):
-self.initial_pause = initial_pause
+self.pause_initial = pause_initial
+self.pause_between = pause_between
 self.num_transfers = num_transfers
 self.transfer_size = transfer_size
 self.continuous_transfers = continuous_transfers
-self.inter_transfer_pause = inter_transfer_pause
 self.port = port
 self.servers = servers
 self.socks_port = socks_port
@@ -98,20 +98,17 @@ class TorperfModel(GeneratableTGenModel):
loglevel="info",
heartbeat="1 minute")
 
-g.add_node("pause", time="%d seconds" % self.config.initial_pause)
-g.add_edge("start", "pause")
-
-# "One-shot mode," i.e., onionperf will stop after the given number of
-# iterations.  The idea is:
-# start -> pause -> stream-1 -> pause-1 -> ... -> stream-n -> pause-n 
-> end
+g.add_node("pause_initial",
+   time="%d seconds" % self.config.pause_initial)
 g.add_node("stream",
sendsize="0",
recvsize=self.config.transfer_size,
timeout="15 seconds",
stallout="10 seconds")
 g.add_node("pause_between",
-   time="%d seconds" % self.config.inter_transfer_pause)
+   time="%d seconds" % self.config.pause_between)
 
+g.add_edge("start", "pause_initial")
 g.add_edge("pause_initial", "stream")
 
 # only add an end node if we need to stop
diff --git a/onionperf/onionperf b/onionperf/onionperf
index d95e691..a49982b 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -190,16 +190,16 @@ def main():
 action="store", dest="tgenconnectport",
 default=8080)
 
-measure_parser.add_argument('--tgen-start-pause',
+measure_parser.add_argument('--tgen-pause-initial',
 help="""the number of seconds TGen should wait before walking through 
its action graph""",
 metavar="N", type=int,
-action="store", dest="tgenstartpause",
+action="store", dest="tgenpauseinitial",
 default=5)
 
-measure_parser.add_argument('--tgen-intertransfer-pause',
+measure_parser.add_argument('--tgen-pause-between',
 help="""the number of seconds TGen should wait in between two 
transfers""",
 metavar="N", type=int,
-action="store", dest="tgenintertransferpause",
+action="store", dest="tgenpausebetween",
 default=300)
 
 measure_parser.add_argument('--tgen-transfer-size',
@@ -377,11 +377,11 @@ def measure(args):
 tor_ctl_port=server_tor_ctl_port,
 tor_socks_port=server_tor_socks_port)
 
-tgen_model = TGenModelConf(initial_pause=args.tgenstartpause,
+tgen_model = TGenModelConf(pause_initial=args.tgenpauseinitial,
transfer_size=args.tgentransfersize,
num_transfers=args.tgennumtransfers,
continuous_transfers=args.tgennumtransfers 
== 0,
-   
inter_transfer_pause=args.tgenintertransferpause)
+   pause_between=args.tgenpausebetween)
 
 meas = Measurement(args.torpath,
args.tgenpath,



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Let TGen client finish by itself in one-shot mode.

2020-08-27 Thread karsten
commit 959cf3689106189001a83c7e58dc40e10497a081
Author: Philipp Winter 
Date:   Fri Aug 7 14:48:58 2020 -0700

Let TGen client finish by itself in one-shot mode.

We tell TGen client to finish on its own by passing the count option to
the end node:
https://github.com/shadow/tgen/blob/master/doc/TGen-Options.md#end-options

This patch adds another argument to the function watchdog_thread_task(),
no_relaunch, which instructs the function to not re-launch its process
if it fails.
---
 onionperf/measurement.py | 45 +++--
 onionperf/model.py   |  3 ++-
 2 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/onionperf/measurement.py b/onionperf/measurement.py
index e2d8d1c..d699292 100644
--- a/onionperf/measurement.py
+++ b/onionperf/measurement.py
@@ -50,10 +50,11 @@ def readline_thread_task(instream, q):
 # wait for lines from stdout until the EOF
 for line in iter(instream.readline, b''): q.put(line)
 
-def watchdog_thread_task(cmd, cwd, writable, done_ev, send_stdin, 
ready_search_str, ready_ev):
+def watchdog_thread_task(cmd, cwd, writable, done_ev, send_stdin, 
ready_search_str, ready_ev, no_relaunch):
 
-# launch or re-launch our sub process until we are told to stop
-# if we fail too many times in too short of time, give up and exit
+# launch or re-launch (or don't re-launch, if no_relaunch is set) our sub
+# process until we are told to stop if we fail too many times in too short
+# of time, give up and exit
 failure_times = []
 pause_time_seconds = 0
 while done_ev.is_set() is False:
@@ -105,6 +106,10 @@ def watchdog_thread_task(cmd, cwd, writable, done_ev, 
send_stdin, ready_search_s
 subp.wait()
 elif done_ev.is_set():
 logging.info("command '{}' finished as expected".format(cmd))
+elif no_relaunch:
+logging.info("command '{}' finished on its own".format(cmd))
+# our command finished on its own. time to terminate.
+done_ev.set()
 else:
 logging.warning("command '{}' finished before 
expected".format(cmd))
 now = time.time()
@@ -284,15 +289,9 @@ class Measurement(object):
 time.sleep(1)
 while True:
 if tgen_model.num_transfers:
-downloads = 0
-while True:
-downloads = 
self.__get_download_count(tgen_client_writable.filename)
-time.sleep(1)
-if downloads >= tgen_model.num_transfers:
-logging.info("Onionperf has downloaded %d 
files and will now shut down." % tgen_model.num_transfers)
-break
-else:
-continue
+# This function blocks until our TGen client process
+# terminated on its own.
+self.__wait_for_tgen_client()
 break
 
 if self.__is_alive():
@@ -366,7 +365,10 @@ class Measurement(object):
 logging.info("Logging TGen {1} process output to 
{0}".format(tgen_logpath, name))
 
 tgen_cmd = "{0} {1}".format(self.tgen_bin_path, tgen_confpath)
-tgen_args = (tgen_cmd, tgen_datadir, tgen_writable, self.done_event, 
None, None, None)
+# If we're running in "one-shot mode", TGen client will terminate on
+# its own and we don't need our watchdog to restart the process.
+no_relaunch = (name == "client" and tgen_model_conf.num_transfers)
+tgen_args = (tgen_cmd, tgen_datadir, tgen_writable, self.done_event, 
None, None, None, no_relaunch)
 tgen_watchdog = threading.Thread(target=watchdog_thread_task, 
name="tgen_{0}_watchdog".format(name), args=tgen_args)
 tgen_watchdog.start()
 self.threads.append(tgen_watchdog)
@@ -464,7 +466,7 @@ WarnUnsafeSocks 0\nSafeLogging 0\nMaxCircuitDirtiness 60 
seconds\nDataDirectory
 tor_stdin_bytes = str_tools._to_bytes(tor_config)
 tor_ready_str = "Bootstrapped 100"
 tor_ready_ev = threading.Event()
-tor_args = (tor_cmd, tor_datadir, tor_writable, self.done_event, 
tor_stdin_bytes, tor_ready_str, tor_ready_ev)
+tor_args = (tor_cmd, tor_datadir, tor_writable, self.done_event, 
tor_stdin_bytes, tor_ready_str, tor_ready_ev, False)
 tor_watchdog = threading.Thread(target=watchdog_thread_task, 
name="tor_{0}_watchdog".format(name), args=tor_args)
 tor_watchdog.start()
 self.threads.append(tor_watchdog)
@@ -491,14 +493,13 @@ WarnUnsafeSocks 0\nSafeLogging 0\nMaxCircuitDirtiness 60 
seconds\nDataDirectory
 
 return tor_writable, torctl_writable
 
-def __get_download_count(self, tgen_logpath):
-count = 0
-if tgen_logpath is not None and os.path.exists(tgen_logpath):
- 

[tor-commits] [onionperf/develop] Apply 1 suggestion(s) to 1 file(s)

2020-08-27 Thread karsten
commit a6aa4189e04ee1b05dbdb68b90f24e14bf3443ac
Author: Philipp Winter 
Date:   Fri Aug 14 16:47:04 2020 +

Apply 1 suggestion(s) to 1 file(s)
---
 onionperf/model.py | 54 ++
 1 file changed, 22 insertions(+), 32 deletions(-)

diff --git a/onionperf/model.py b/onionperf/model.py
index bdd5a53..b589249 100644
--- a/onionperf/model.py
+++ b/onionperf/model.py
@@ -104,40 +104,30 @@ class TorperfModel(GeneratableTGenModel):
 # "One-shot mode," i.e., onionperf will stop after the given number of
 # iterations.  The idea is:
 # start -> pause -> stream-1 -> pause-1 -> ... -> stream-n -> pause-n 
-> end
-if self.config.num_transfers > 0:
-for i in range(self.config.num_transfers):
-g.add_node("stream-%d" % i,
-   sendsize="0",
-   recvsize=self.config.transfer_size,
-   timeout="15 seconds",
-   stallout="10 seconds")
-g.add_node("pause-%d" % i,
-   time="%d seconds" % 
self.config.inter_transfer_pause)
-
-g.add_edge("stream-%d" % i, "pause-%d" % i)
-if i > 0:
-g.add_edge("pause-%d" % (i-1), "stream-%d" % i)
-
+g.add_node("stream",
+   sendsize="0",
+   recvsize=self.config.transfer_size,
+   timeout="15 seconds",
+   stallout="10 seconds")
+g.add_node("pause_between",
+   time="%d seconds" % self.config.inter_transfer_pause)
+
+g.add_edge("pause_initial", "stream")
+
+# only add an end node if we need to stop
+if self.config.continuous_transfers:
+# continuous mode, i.e., no end node
+g.add_edge("stream", "pause_between")
+else:
+# one-shot mode, i.e., end after configured number of transfers
 g.add_node("end",
count=str(self.config.num_transfers))
-g.add_edge("pause", "stream-0")
-g.add_edge("pause-%d" % (self.config.num_transfers - 1), "end")
-
-# Continuous mode, i.e., onionperf will not stop.  The idea is:
-# start -> pause -> stream -> pause
-#   ^   |
-#   +---+
-elif self.config.continuous_transfers:
-g.add_node("stream",
-   sendsize="0",
-   recvsize=self.config.transfer_size,
-   timeout="15 seconds",
-   stallout="10 seconds")
-g.add_node("pause",
-   time="%d seconds" % self.config.inter_transfer_pause)
-g.add_edge("pause", "stream")
-g.add_edge("stream", "pause")
-g.add_edge("pause", "stream")
+# check for end condition after every transfer
+g.add_edge("stream", "end")
+# if end condition not met, pause
+g.add_edge("end", "pause_between")
+
+g.add_edge("pause_between", "stream")
 
 return g
 



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Make models more configurable.

2020-08-27 Thread karsten
commit 7ef8c64833d41337d5c9cc5baaee2808092c9aad
Author: Philipp Winter 
Date:   Fri Jun 26 10:00:29 2020 -0700

Make models more configurable.

This patch removes the --oneshot subcommand and replaces it with several
new subcommands for OnionPerf's "measure" command:

--tgen-start-pause  (Initial pause before file transfers.)
--tgen-num-transfers(Number of file transfers.)
--tgen-intertransfer-pause  (Pause in between file transfers.)
--tgen-transfer-size(Size of each file transfer.)

By default, OnionPerf continues to run in "continuous" mode.  One can
simulate oneshot mode by running onionperf with the following flags:

onionperf measure --tgen-num-transfers=1

In addition to the above subcommands, this patch improves the code base
by 1) adding a TGenConf class to hold TGen's configuration and by 2)
adding a TGenModelConf class to hold TGen's traffic model.

This fixes tpo/metrics/onionperf#33432.
---
 onionperf/measurement.py| 102 +++---
 onionperf/model.py  | 108 +++-
 onionperf/onionperf |  60 +++-
 onionperf/tests/test_measurement.py |  12 ++--
 4 files changed, 175 insertions(+), 107 deletions(-)

diff --git a/onionperf/measurement.py b/onionperf/measurement.py
index af1fa0d..e2d8d1c 100644
--- a/onionperf/measurement.py
+++ b/onionperf/measurement.py
@@ -15,6 +15,16 @@ from stem.control import Controller
 from stem.version import Version, Requirement, get_system_tor_version
 from stem import __version__ as stem_version
 
+class TGenConf(object):
+"""Represents a TGen configuration, for both client and server."""
+def __init__(self, listen_port=None, connect_ip=None, connect_port=None, 
tor_ctl_port=None, tor_socks_port=None):
+self.listen_port = str(listen_port)
+self.tor_ctl_port = tor_ctl_port
+self.tor_socks_port = tor_socks_port
+# TGen clients use connect_ip and connect_port.
+self.connect_ip = connect_ip
+self.connect_port = connect_port
+
 # onionperf imports
 from . import analysis, monitor, model, util
 
@@ -173,12 +183,11 @@ def logrotate_thread_task(writables, tgen_writable, 
torctl_writable, docroot, ni
 
 class Measurement(object):
 
-def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, oneshot, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False):
+def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False):
 self.tor_bin_path = tor_bin_path
 self.tgen_bin_path = tgen_bin_path
 self.datadir_path = datadir_path
 self.privatedir_path = privatedir_path
-self.oneshot = oneshot
 self.nickname = nickname
 self.threads = None
 self.done_event = None
@@ -190,20 +199,30 @@ class Measurement(object):
 self.torserver_conf_file = torserver_conf_file
 self.single_onion = single_onion
 
-def run(self, do_onion=True, do_inet=True, client_tgen_listen_port=5, 
client_tgen_connect_ip='0.0.0.0', client_tgen_connect_port=8080, 
client_tor_ctl_port=59050, client_tor_socks_port=59000,
- server_tgen_listen_port=8080, server_tor_ctl_port=59051, 
server_tor_socks_port=59001):
+def run(self, do_onion=True, do_inet=True, tgen_model=None, 
tgen_client_conf=None, tgen_server_conf=None):
 '''
-only `server_tgen_listen_port` are "public" and need to be opened on 
the firewall.
-if `client_tgen_connect_port` != `server_tgen_listen_port`, then you 
should have installed a forwarding rule in the firewall.
+only `tgen_server_conf.listen_port` are "public" and need to be opened 
on the firewall.
+if `tgen_client_conf.connect_port` != `tgen_server_conf.listen_port`, 
then you should have installed a forwarding rule in the firewall.
 all ports need to be unique though, and unique among multiple 
onionperf instances.
 
 here are some sane defaults:
-client_tgen_listen_port=5, client_tgen_connect_port=8080, 
client_tor_ctl_port=59050, client_tor_socks_port=59000,
-server_tgen_listen_port=8080, server_tor_ctl_port=59051, 
server_tor_socks_port=59001
+tgen_client_conf.listen_port=5, 
tgen_client_conf.connect_port=8080, tgen_client_conf.tor_ctl_port=59050, 
tgen_client_conf.tor_socks_port=59000,
+tgen_server_conf.listen_port=8080, 
tgen_server_conf.tor_ctl_port=59051, tgen_server_conf.tor_socks_port=59001
 '''
 self.threads = []
 self.done_event = threading.Event()
 
+if tgen_client_conf is None:
+tgen_client_conf = TGenConf(listen_port=5,
+   

[tor-commits] [onionperf/develop] Make some tweaks to new TGen model.

2020-08-27 Thread karsten
commit b8f1e5c2695c097a7494f7975403664c0c833825
Author: Karsten Loesing 
Date:   Sun Aug 16 22:03:34 2020 +0200

Make some tweaks to new TGen model.

 - Change timeout back to 270 seconds and stallout back to 0 seconds.
 - Change initial pause to 300 seconds to keep default behavior
   unchanged.
 - Change model, so that pause_between starts in parallel to a stream,
   not when the stream is completed. This is the same behavior as
   before.

Also add a change log entry for all changes.
---
 CHANGELOG.md|  7 +++
 onionperf/model.py  | 20 
 onionperf/onionperf |  2 +-
 3 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c4c4f2..ac6897b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+# Changes in version 0.7 - 2020-??-??
+
+ - Remove the `onionperf measure --oneshot` switch and replace it with
+   new switches `--tgen-pause-initial`, `--tgen-pause-between`,
+   `--tgen-transfer-size`, and `--tgen-num-transfers ` to further
+   configure the generated TGen model.
+
 # Changes in version 0.6 - 2020-??-??
 
  - Update to TGen 1.0.0, use TGenTools for parsing TGen log files, and
diff --git a/onionperf/model.py b/onionperf/model.py
index d45763e..fde587f 100644
--- a/onionperf/model.py
+++ b/onionperf/model.py
@@ -43,8 +43,8 @@ class TGenLoadableModel(TGenModel):
 
 class TGenModelConf(object):
 """Represents a TGen traffic model configuration."""
-def __init__(self, pause_initial=0, num_transfers=1, transfer_size="5 MiB",
- continuous_transfers=False, pause_between=5, port=None, 
servers=[],
+def __init__(self, pause_initial=300, num_transfers=1, transfer_size="5 
MiB",
+ continuous_transfers=False, pause_between=300, port=None, 
servers=[],
  socks_port=None):
 self.pause_initial = pause_initial
 self.pause_between = pause_between
@@ -103,28 +103,24 @@ class TorperfModel(GeneratableTGenModel):
 g.add_node("stream",
sendsize="0",
recvsize=self.config.transfer_size,
-   timeout="15 seconds",
-   stallout="10 seconds")
+   timeout="270 seconds",
+   stallout="0 seconds")
 g.add_node("pause_between",
time="%d seconds" % self.config.pause_between)
 
 g.add_edge("start", "pause_initial")
 g.add_edge("pause_initial", "stream")
+g.add_edge("pause_initial", "pause_between")
+g.add_edge("pause_between", "stream")
+g.add_edge("pause_between", "pause_between")
 
 # only add an end node if we need to stop
-if self.config.continuous_transfers:
-# continuous mode, i.e., no end node
-g.add_edge("stream", "pause_between")
-else:
+if not self.config.continuous_transfers:
 # one-shot mode, i.e., end after configured number of transfers
 g.add_node("end",
count="%d" % self.config.num_transfers)
 # check for end condition after every transfer
 g.add_edge("stream", "end")
-# if end condition not met, pause
-g.add_edge("end", "pause_between")
-
-g.add_edge("pause_between", "stream")
 
 return g
 
diff --git a/onionperf/onionperf b/onionperf/onionperf
index a49982b..6a16da2 100755
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@ -194,7 +194,7 @@ def main():
 help="""the number of seconds TGen should wait before walking through 
its action graph""",
 metavar="N", type=int,
 action="store", dest="tgenpauseinitial",
-default=5)
+default=300)
 
 measure_parser.add_argument('--tgen-pause-between',
 help="""the number of seconds TGen should wait in between two 
transfers""",



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Use format string for consistency.

2020-08-27 Thread karsten
commit 1eea5e10700c76f8e1b37e626eaeaf96c5488150
Author: Philipp Winter 
Date:   Fri Aug 14 11:29:01 2020 -0700

Use format string for consistency.

Thanks to Rob for pointing this out.
---
 onionperf/model.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/onionperf/model.py b/onionperf/model.py
index 3bfe35f..d45763e 100644
--- a/onionperf/model.py
+++ b/onionperf/model.py
@@ -118,7 +118,7 @@ class TorperfModel(GeneratableTGenModel):
 else:
 # one-shot mode, i.e., end after configured number of transfers
 g.add_node("end",
-   count=str(self.config.num_transfers))
+   count="%d" % self.config.num_transfers)
 # check for end condition after every transfer
 g.add_edge("stream", "end")
 # if end condition not met, pause



___
tor-commits mailing list
tor-commits@lists.torproject.org
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits


[tor-commits] [onionperf/develop] Merge branch 'phw-enhancement-33432-3' into develop

2020-08-27 Thread karsten
commit c707674ba9b0d7931038c12bbe2d01585a88eb22
Merge: dfec0b8 b8f1e5c
Author: Karsten Loesing 
Date:   Thu Aug 27 10:38:21 2020 +0200

Merge branch 'phw-enhancement-33432-3' into develop

 CHANGELOG.md|   4 ++
 onionperf/measurement.py| 139 +++-
 onionperf/model.py  |  98 ++---
 onionperf/onionperf |  60 
 onionperf/tests/test_measurement.py |  12 ++--
 5 files changed, 185 insertions(+), 128 deletions(-)

diff --cc CHANGELOG.md
index c31a40e,ac6897b..4b7fcb2
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@@ -1,9 -1,11 +1,13 @@@
  # Changes in version 0.7 - 2020-??-??
  
 + - Add `onionperf measure --drop-guards` parameter to use and drop
 +   guards after a given number of hours. Implements #33399.
+  - Remove the `onionperf measure --oneshot` switch and replace it with
+new switches `--tgen-pause-initial`, `--tgen-pause-between`,
+`--tgen-transfer-size`, and `--tgen-num-transfers ` to further
+configure the generated TGen model.
  
 -# Changes in version 0.6 - 2020-??-??
 +# Changes in version 0.6 - 2020-08-08
  
   - Update to TGen 1.0.0, use TGenTools for parsing TGen log files, and
 update analysis results file version to 3.0. Implements #33974.
diff --cc onionperf/measurement.py
index 709fbc6,d699292..f198be0
--- a/onionperf/measurement.py
+++ b/onionperf/measurement.py
@@@ -173,7 -188,7 +188,7 @@@ def logrotate_thread_task(writables, tg
  
  class Measurement(object):
  
- def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, oneshot, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False, 
drop_guards_interval_hours=0):
 -def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False):
++def __init__(self, tor_bin_path, tgen_bin_path, datadir_path, 
privatedir_path, nickname, additional_client_conf=None, 
torclient_conf_file=None, torserver_conf_file=None, single_onion=False, 
drop_guards_interval_hours=0):
  self.tor_bin_path = tor_bin_path
  self.tgen_bin_path = tgen_bin_path
  self.datadir_path = datadir_path
@@@ -189,13 -203,11 +203,12 @@@
  self.torclient_conf_file = torclient_conf_file
  self.torserver_conf_file = torserver_conf_file
  self.single_onion = single_onion
 +self.drop_guards_interval_hours = drop_guards_interval_hours
  
- def run(self, do_onion=True, do_inet=True, client_tgen_listen_port=5, 
client_tgen_connect_ip='0.0.0.0', client_tgen_connect_port=8080, 
client_tor_ctl_port=59050, client_tor_socks_port=59000,
-  server_tgen_listen_port=8080, server_tor_ctl_port=59051, 
server_tor_socks_port=59001):
+ def run(self, do_onion=True, do_inet=True, tgen_model=None, 
tgen_client_conf=None, tgen_server_conf=None):
  '''
- only `server_tgen_listen_port` are "public" and need to be opened on 
the firewall.
- if `client_tgen_connect_port` != `server_tgen_listen_port`, then you 
should have installed a forwarding rule in the firewall.
+ only `tgen_server_conf.listen_port` are "public" and need to be 
opened on the firewall.
+ if `tgen_client_conf.connect_port` != `tgen_server_conf.listen_port`, 
then you should have installed a forwarding rule in the firewall.
  all ports need to be unique though, and unique among multiple 
onionperf instances.
  
  here are some sane defaults:
diff --cc onionperf/onionperf
index 641db70,6a16da2..e6aa44a
--- a/onionperf/onionperf
+++ b/onionperf/onionperf
@@@ -195,12 -190,30 +190,36 @@@ def main()
  action="store", dest="tgenconnectport",
  default=8080)
  
+ measure_parser.add_argument('--tgen-pause-initial',
+ help="""the number of seconds TGen should wait before walking through 
its action graph""",
+ metavar="N", type=int,
+ action="store", dest="tgenpauseinitial",
+ default=300)
+ 
+ measure_parser.add_argument('--tgen-pause-between',
+ help="""the number of seconds TGen should wait in between two 
transfers""",
+ metavar="N", type=int,
+ action="store", dest="tgenpausebetween",
+ default=300)
+ 
+ measure_parser.add_argument('--tgen-transfer-size',
+ help="""the size of the file transfer that TGen will perform (e.g., 
'5 MiB' or '10 KiB')""",
+ metavar="STRING", type=str,
+ action="store", dest="tgentransfersize",
+ default="5 MiB")
+ 
+ measure_parser.add_argument('--tgen-num-transfers',

  1   2   3   4   5   6   7   8   9   10   >