MLab/NDT: Prep for connecting to wizard, fixes, cleanups

This commit is contained in:
zzz
2018-11-14 14:48:10 +00:00
parent a35ad5fc57
commit 6462e2a292
5 changed files with 200 additions and 40 deletions

View File

@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import com.vuze.plugins.mlab.tools.ndt.swingemu.Tcpbw100UIWrapper; import com.vuze.plugins.mlab.tools.ndt.swingemu.Tcpbw100UIWrapper;
import com.vuze.plugins.mlab.tools.ndt.swingemu.Tcpbw100UIWrapperListener; import com.vuze.plugins.mlab.tools.ndt.swingemu.Tcpbw100UIWrapperListener;
@ -40,7 +41,6 @@ import net.minidev.json.parser.ParseException;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.router.RouterContext;
import net.i2p.util.EepGet; import net.i2p.util.EepGet;
import net.i2p.util.I2PAppThread; import net.i2p.util.I2PAppThread;
import net.i2p.util.Log; import net.i2p.util.Log;
@ -51,13 +51,14 @@ import net.i2p.util.Log;
* @since 0.9.38 * @since 0.9.38
*/ */
public class MLabRunner { public class MLabRunner {
// ns.measurementlab.net does not support https
// use ndt_ssl for test over ssl? but Tcpbw100 doesn't support it
private static final String NS_URL = "http://ns.measurementlab.net/ndt?format=json"; private static final String NS_URL = "http://ns.measurementlab.net/ndt?format=json";
private static final long NS_TIMEOUT = 20*1000; private static final long NS_TIMEOUT = 20*1000;
private boolean test_active; private boolean test_active;
private final I2PAppContext _context; private final I2PAppContext _context;
// null for testing
private final RouterContext _rcontext;
private final Log _log; private final Log _log;
private final AtomicBoolean _running = new AtomicBoolean();
private static MLabRunner _instance; private static MLabRunner _instance;
public static MLabRunner getInstance(I2PAppContext ctx) { public static MLabRunner getInstance(I2PAppContext ctx) {
@ -70,13 +71,26 @@ public class MLabRunner {
private MLabRunner(I2PAppContext ctx) { private MLabRunner(I2PAppContext ctx) {
_context = ctx; _context = ctx;
_rcontext = ctx.isRouterContext() ? (RouterContext) ctx : null;
_log = ctx.logManager().getLog(MLabRunner.class); _log = ctx.logManager().getLog(MLabRunner.class);
} }
public boolean isRunning() {
return _running.get();
}
/**
* Non-blocking, spawns a thread and returns immediately.
*
* @param listener use to detect completion and get results
* @return a ToolRun object which may be used to cancel the test,
* or null if there was already a test in progress.
*/
public ToolRun runNDT(final ToolListener listener) { public ToolRun runNDT(final ToolListener listener) {
if (!_running.compareAndSet(false, true)) {
_log.warn("Test already running");
return null;
}
final ToolRun run = new ToolRunImpl(); final ToolRun run = new ToolRunImpl();
//final AESemaphore sem = new AESemaphore( "waiter" );
runTool( runTool(
new Runnable() new Runnable()
@ -85,7 +99,6 @@ public class MLabRunner {
boolean completed = false; boolean completed = false;
try{ try{
_log.warn("Starting NDT Test"); _log.warn("Starting NDT Test");
_log.warn("-----------------");
new Tcpbw100UIWrapper( new Tcpbw100UIWrapper(
new Tcpbw100UIWrapperListener() new Tcpbw100UIWrapperListener()
@ -141,7 +154,6 @@ public class MLabRunner {
// public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, // public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort,
// int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, // int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream,
// String url, boolean allowCaching, String etag, String postData) { // String url, boolean allowCaching, String etag, String postData) {
// TODO why no HTTPS?
EepGet eepget = new EepGet(_context, false, null, 0, EepGet eepget = new EepGet(_context, false, null, 0,
0, 2, 1024, null, baos, 0, 2, 1024, null, baos,
NS_URL, false, null, null); NS_URL, false, null, null);
@ -160,6 +172,7 @@ public class MLabRunner {
if (_log.shouldWarn()) if (_log.shouldWarn())
_log.warn("Got response: " + DataHelper.getUTF8(b)); _log.warn("Got response: " + DataHelper.getUTF8(b));
// TODO use IP instead to avoid another lookup? // TODO use IP instead to avoid another lookup?
// or use "fqdn" in response instead of "url"
URL url = new URL((String)map.get( "url" )); URL url = new URL((String)map.get( "url" ));
if (url == null) { if (url == null) {
throw new IOException("no url"); throw new IOException("no url");
@ -174,7 +187,7 @@ public class MLabRunner {
} }
if (server_host == null) { if (server_host == null) {
// fallback to old, discouraged approach // fallback to old, discouraged approach
server_host = "ndt.iupui.donar.measurement-lab.org"; server_host = "ndt.iupui.donar.measurement-lab.org";
if (_log.shouldWarn()) if (_log.shouldWarn())
_log.warn("Failed to select server, falling back to donar method"); _log.warn("Failed to select server, falling back to donar method");
@ -191,8 +204,6 @@ public class MLabRunner {
} }
}); });
//sem.release();
test.runIt(); test.runIt();
try { Thread.sleep(2000); } catch (InterruptedException ie) { return; } try { Thread.sleep(2000); } catch (InterruptedException ie) { return; }
@ -202,11 +213,13 @@ public class MLabRunner {
try { Thread.sleep(1000); } catch (InterruptedException ie) { break; } try { Thread.sleep(1000); } catch (InterruptedException ie) { break; }
} }
// in integer bytes per second
long up_bps = 0; long up_bps = 0;
try { try {
up_bps = (long)(Double.parseDouble(test.get_c2sspd())*1000000)/8; up_bps = (long)(Double.parseDouble(test.get_c2sspd())*1000000)/8;
} catch(Throwable e) {} } catch(Throwable e) {}
// in integer bytes per second
long down_bps = 0; long down_bps = 0;
try { try {
down_bps = (long)(Double.parseDouble(test.get_s2cspd())*1000000)/8; down_bps = (long)(Double.parseDouble(test.get_s2cspd())*1000000)/8;
@ -236,54 +249,44 @@ public class MLabRunner {
_log.warn("Test complete in " + DataHelper.formatDuration(end - start)); _log.warn("Test complete in " + DataHelper.formatDuration(end - start));
} }
} finally { } finally {
//sem.release();
if (!completed && listener != null) { if (!completed && listener != null) {
listener.complete( new HashMap<String, Object>()); listener.complete( new HashMap<String, Object>());
} }
_running.set(false);
} }
} }
}); });
//sem.reserve();
return run; return run;
} }
protected void runTool(final Runnable target) { /**
// need something like this * Non-blocking, spawns a thread and returns immediately.
//ap.setEnabled( false ); */
new I2PAppThread("toolRunner") private void runTool(final Runnable target) {
new I2PAppThread("MLabRunner")
{ {
@Override @Override
public void run() { public void run() {
try{ try{
target.run(); target.run();
}finally{ }finally{
//ap.setEnabled( true );
} }
} }
}.start(); }.start();
} }
public void runTest( /**
final Map<String,Object> args, * Returned from runNDT
//final IPCInterface callback, */
final boolean autoApply)
throws Exception
{
synchronized( this ){
if (test_active) {
throw new Exception("Test already active");
}
test_active = true;
}
}
public interface ToolRun { public interface ToolRun {
public void cancel(); public void cancel();
public void addListener(ToolRunListener l); public void addListener(ToolRunListener l);
} }
/**
* Returned from runNDT
*/
private class ToolRunImpl implements ToolRun { private class ToolRunImpl implements ToolRun {
private List<ToolRunListener> listeners = new ArrayList<ToolRunListener>(); private List<ToolRunListener> listeners = new ArrayList<ToolRunListener>();
private boolean cancelled; private boolean cancelled;
@ -319,10 +322,12 @@ public class MLabRunner {
} }
} }
/** The listener for ToolRun */
public interface ToolRunListener { public interface ToolRunListener {
public void cancelled(); public void cancelled();
} }
/** The parameter for runNDT() */
public interface ToolListener { public interface ToolListener {
public void reportSummary(String str); public void reportSummary(String str);
public void reportDetail(String str); public void reportDetail(String str);
@ -331,6 +336,8 @@ public class MLabRunner {
/** standalone test */ /** standalone test */
private static class TestListener implements ToolListener { private static class TestListener implements ToolListener {
private final AtomicBoolean _complete = new AtomicBoolean();
public void reportSummary(String str) { public void reportSummary(String str) {
System.out.println(str); System.out.println(str);
} }
@ -341,6 +348,11 @@ public class MLabRunner {
public void complete(Map<String, Object> results) { public void complete(Map<String, Object> results) {
System.out.println("**************** Results: " + DataHelper.toString(results) + "***********************"); System.out.println("**************** Results: " + DataHelper.toString(results) + "***********************");
_complete.set(true);
}
public boolean isComplete() {
return _complete.get();
} }
} }
@ -348,7 +360,13 @@ public class MLabRunner {
public static void main(String[] args) { public static void main(String[] args) {
I2PAppContext ctx = I2PAppContext.getGlobalContext(); I2PAppContext ctx = I2PAppContext.getGlobalContext();
MLabRunner mlab = MLabRunner.getInstance(ctx); MLabRunner mlab = MLabRunner.getInstance(ctx);
ToolListener lsnr = new TestListener(); TestListener lsnr = new TestListener();
mlab.runNDT(lsnr); mlab.runNDT(lsnr);
try { Thread.sleep(2000); } catch (InterruptedException ie) { return; }
for (int i = 0; i < 180; i++) {
if (lsnr.isComplete())
break;
try { Thread.sleep(1000); } catch (InterruptedException ie) { break; }
}
} }
} }

View File

@ -2,6 +2,9 @@ package net.i2p.router.web.helpers;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import com.vuze.plugins.mlab.MLabRunner;
import net.i2p.router.Router; import net.i2p.router.Router;
import net.i2p.router.transport.FIFOBandwidthRefiller; import net.i2p.router.transport.FIFOBandwidthRefiller;
@ -9,10 +12,23 @@ import net.i2p.router.web.FormHandler;
/** /**
* The new user wizard. * The new user wizard.
* This bean has SESSION scope so the results may be retrieved.
* All necessary methods are synchronized.
* *
* @since 0.9.38 * @since 0.9.38
*/ */
public class WizardHandler extends FormHandler { public class WizardHandler extends FormHandler {
// session scope, but it's an underlying singleton
private MLabRunner _mlab;
// session scope
private TestListener _listener;
@Override
public void setContextId(String contextId) {
super.setContextId(contextId);
_mlab = MLabRunner.getInstance(_context);
}
@Override @Override
protected void processForm() { protected void processForm() {
@ -99,4 +115,121 @@ public class WizardHandler extends FormHandler {
} }
return updated; return updated;
} }
public synchronized boolean isNDTComplete() {
return _listener != null && _listener.isComplete();
}
public synchronized boolean isNDTRunning() {
return _listener != null && !_listener.isComplete();
}
/**
* @return status string or null
*/
public synchronized String getCompletionStatus() {
return _listener != null ? _listener.getSummary() : null;
}
/**
* @return status string or null
*/
public synchronized String getDetailStatus() {
return _listener != null ? _listener.getDetail() : null;
}
/**
* @return bytes per second or 0
*/
public long getUpBandwidth() {
return getLongResult("up");
}
/**
* @return bytes per second or 0
*/
public long getDownBandwidth() {
return getLongResult("down");
}
public synchronized long getLongResult(String key) {
if (_listener != null) {
Map<String, Object> results = _listener.getResults();
if (results != null) {
Long v = (Long) results.get(key);
if (v != null)
return v.longValue();
}
}
return 0;
}
/** start the test */
public synchronized void startNDT() {
if (_mlab.isRunning() || _listener != null && !_listener.isComplete()) {
addFormError(_t("Bandwidth test is already running"));
return;
}
_listener = new TestListener();
MLabRunner.ToolRun runner = _mlab.runNDT(_listener);
if (runner != null) {
addFormNotice(_t("Started bandwidth test"));
} else {
Map<String, Object> map = new HashMap<String, Object>(2);
_listener.complete(map);
addFormError(_t("Bandwidth test is already running"));
}
}
/** cancel the test */
public synchronized void cancelNDT() {
synchronized(WizardHandler.class) {
if (!_mlab.isRunning()) {
addFormError(_t("Bandwidth test was not running"));
return;
}
/****
TODO
if (runner != null)
addFormNotice(_t("Started bandwidth test"));
else
addFormError(_t("Bandwidth test is already running"));
****/
}
}
/** test results */
private static class TestListener implements MLabRunner.ToolListener {
private String _summary, _detail;
private Map<String, Object> _results;
public synchronized void reportSummary(String str) {
_summary = str;
}
public synchronized void reportDetail(String str) {
_detail = str;
}
public synchronized void complete(Map<String, Object> results) {
_results = results;
}
public synchronized boolean isComplete() {
return _results != null;
}
public synchronized String getSummary() {
return _summary;
}
public synchronized String getDetail() {
return _detail;
}
public synchronized Map<String, Object> getResults() {
return _results;
}
}
} }

View File

@ -9,7 +9,9 @@ import net.i2p.router.web.HelperBase;
*/ */
public class WizardHelper extends HelperBase { public class WizardHelper extends HelperBase {
public static final String PROP_COMPLETE = "routerconsole.welcomeWizardComplete";
public void complete() { public void complete() {
_context.router().saveConfig("routerconsole.welcomeWizardComplete", "true"); _context.router().saveConfig(PROP_COMPLETE, "true");
} }
} }

View File

@ -12,6 +12,7 @@
// while preserving any query parameters // while preserving any query parameters
// //
response.setStatus(307); response.setStatus(307);
response.setHeader("Cache-Control","no-cache");
String req = request.getRequestURL().toString(); String req = request.getRequestURL().toString();
StringBuilder buf = new StringBuilder(128); StringBuilder buf = new StringBuilder(128);
if (req.endsWith("index")) if (req.endsWith("index"))
@ -23,13 +24,13 @@
buf.append('/'); buf.append('/');
net.i2p.I2PAppContext ctx = net.i2p.I2PAppContext.getGlobalContext(); net.i2p.I2PAppContext ctx = net.i2p.I2PAppContext.getGlobalContext();
boolean oldHome = ctx.getBooleanProperty("routerconsole.oldHomePage"); boolean oldHome = ctx.getBooleanProperty("routerconsole.oldHomePage");
boolean wizRun = ctx.getBooleanProperty("routerconsole.welcomeWizardComplete"); boolean wizRun = ctx.getBooleanProperty(net.i2p.router.web.helpers.WizardHelper.PROP_COMPLETE);
String firstVersion = ctx.getProperty("router.firstVersion"); String firstVersion = ctx.getProperty("router.firstVersion");
String tgt; String tgt;
final boolean ENABLE_WIZARD_ON_FIRST_RUN = false; final boolean ENABLE_WIZARD_ON_FIRST_RUN = false;
if (oldHome) { if (oldHome) {
tgt = "console"; tgt = "console";
} else if (ENABLE_WIZARD_ON_FIRST_RUN && (wizRun || firstVersion == null)) { } else if (!ENABLE_WIZARD_ON_FIRST_RUN || wizRun || firstVersion == null) {
// wizard already run // wizard already run
tgt = "home"; tgt = "home";
} else { } else {
@ -52,4 +53,4 @@
response.setHeader("Location", buf.toString()); response.setHeader("Location", buf.toString());
// force commitment // force commitment
response.getOutputStream().close(); response.getOutputStream().close();
%> %>

View File

@ -42,6 +42,7 @@
// redirect to /home // redirect to /home
response.setStatus(307); response.setStatus(307);
response.setHeader("Cache-Control","no-cache");
String req = request.getRequestURL().toString(); String req = request.getRequestURL().toString();
int slash = req.indexOf("/welcome"); int slash = req.indexOf("/welcome");
if (slash >= 0) if (slash >= 0)
@ -63,8 +64,8 @@
<script src="/js/ajax.js" type="text/javascript"></script> <script src="/js/ajax.js" type="text/javascript"></script>
<script type="text/javascript"> <script type="text/javascript">
var failMessage = "<hr><b><%=intl._t("Router is down")%><\/b>"; var failMessage = "<hr><b><%=intl._t("Router is down")%><\/b>";
function requestAjax1() { ajax("/welcomexhr.jsp?requestURI=<%=request.getRequestURI()%>", "xhr", "1000"); } function requestAjax1() { ajax("/welcomexhr.jsp", "xhr", "1000"); }
function initAjax() { setTimeout(requestAjax1, <%=intl.getRefresh()%>000); } function initAjax() { setTimeout(requestAjax1, "1000"); }
</script> </script>
<% <%
} }
@ -83,7 +84,12 @@
%> %>
<h2><%=intl._t("New Install Wizard")%> <%=ipg%>/<%=LAST_PAGE%></h2> <h2><%=intl._t("New Install Wizard")%> <%=ipg%>/<%=LAST_PAGE%></h2>
<div id="wizard"> <div id="wizard">
<jsp:useBean class="net.i2p.router.web.helpers.WizardHandler" id="formhandler" scope="request" /> <%--
// note that for the handler we use a session scope, not a page scope,
// so that we can access the NDT test results.
// The MLabHelper singleton will prevent multiple simultaneous tests, even across sessions.
--%>
<jsp:useBean class="net.i2p.router.web.helpers.WizardHandler" id="formhandler" scope="session" />
<%@include file="formhandler.jsi" %> <%@include file="formhandler.jsi" %>
<form action="" method="POST"> <form action="" method="POST">
<input type="hidden" name="nonce" value="<%=pageNonce%>"> <input type="hidden" name="nonce" value="<%=pageNonce%>">