summaryrefslogtreecommitdiffstats
path: root/dozentenmodulserver/src
diff options
context:
space:
mode:
authorSimon Rettberg2018-05-11 17:36:45 +0200
committerSimon Rettberg2018-05-11 17:36:45 +0200
commitb3557ec2e121f91ca842ca37f0e4b582556046a4 (patch)
tree21f6feb3f678db1da60b6f72fea6ef1cbb72fee0 /dozentenmodulserver/src
parent[client] Fix inverted logic for slowed down hashing (diff)
downloadtutor-module-b3557ec2e121f91ca842ca37f0e4b582556046a4.tar.gz
tutor-module-b3557ec2e121f91ca842ca37f0e4b582556046a4.tar.xz
tutor-module-b3557ec2e121f91ca842ca37f0e4b582556046a4.zip
[server] Support server side chunk copying
Diffstat (limited to 'dozentenmodulserver/src')
-rw-r--r--dozentenmodulserver/src/main/java/fi/iki/elonen/NanoHTTPD.java4
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/SupportedFeatures.java1
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImageBlock.java34
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/IncomingDataTransfer.java17
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/StorageChunkSource.java22
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java213
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java20
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/FileSystem.java32
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java31
9 files changed, 314 insertions, 60 deletions
diff --git a/dozentenmodulserver/src/main/java/fi/iki/elonen/NanoHTTPD.java b/dozentenmodulserver/src/main/java/fi/iki/elonen/NanoHTTPD.java
index b50bdb72..ecb6039b 100644
--- a/dozentenmodulserver/src/main/java/fi/iki/elonen/NanoHTTPD.java
+++ b/dozentenmodulserver/src/main/java/fi/iki/elonen/NanoHTTPD.java
@@ -63,12 +63,14 @@ import java.util.StringTokenizer;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.log4j.Logger;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
+import org.openslx.bwlp.sat.util.PrioThreadFactory;
import org.openslx.util.GrowingThreadPoolExecutor;
/**
@@ -458,7 +460,7 @@ public abstract class NanoHTTPD implements Runnable {
*/
public static class DefaultAsyncRunner implements AsyncRunner {
private ExecutorService pool = new GrowingThreadPoolExecutor(2, 16, 1, TimeUnit.MINUTES,
- new ArrayBlockingQueue<Runnable>(4));
+ new ArrayBlockingQueue<Runnable>(4), new PrioThreadFactory("httpd", Thread.NORM_PRIORITY));
@Override
public void exec(Runnable code) {
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/SupportedFeatures.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/SupportedFeatures.java
index b2ff68e7..2587661b 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/SupportedFeatures.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/SupportedFeatures.java
@@ -10,6 +10,7 @@ public class SupportedFeatures {
registerFeature(Feature.EXTEND_EXPIRED_VM);
registerFeature(Feature.NETWORK_SHARES);
registerFeature(Feature.MULTIPLE_HYPERVISORS);
+ registerFeature(Feature.SERVER_SIDE_COPY);
}
public static String getFeatureString() {
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImageBlock.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImageBlock.java
index 4e68da6d..56685c12 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImageBlock.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImageBlock.java
@@ -11,7 +11,9 @@ import org.apache.log4j.Logger;
import org.openslx.bwlp.sat.database.Database;
import org.openslx.bwlp.sat.database.MysqlConnection;
import org.openslx.bwlp.sat.database.MysqlStatement;
+import org.openslx.bwlp.sat.util.FileSystem;
import org.openslx.filetransfer.FileRange;
+import org.openslx.filetransfer.LocalChunkSource.ChunkSource;
import org.openslx.filetransfer.util.ChunkStatus;
import org.openslx.filetransfer.util.FileChunk;
@@ -36,6 +38,10 @@ public class DbImageBlock {
private static class AsyncThread extends Thread {
private final ArrayBlockingQueue<ChunkUpdate> queue = new ArrayBlockingQueue<>(100);
+
+ public AsyncThread() {
+ super("DbBlockUpdater");
+ }
public void put(ChunkUpdate chunk) throws InterruptedException {
queue.put(chunk);
@@ -174,4 +180,32 @@ public class DbImageBlock {
}
}
+ public static List<ChunkSource> getBlocksWithHash(List<byte[]> sums) throws SQLException {
+ List<ChunkSource> list = null;
+ try (MysqlConnection connection = Database.getConnection()) {
+ MysqlStatement stmt = connection.prepareStatement("SELECT startbyte, blocksize, filepath FROM imageblock"
+ + " INNER JOIN imageversion USING (imageversionid)"
+ + " WHERE blocksha1 = :sha1 GROUP BY imageversionid");
+ for (byte[] sha1 : sums) {
+ stmt.setBinary("sha1", sha1);
+ ResultSet rs = stmt.executeQuery();
+ if (!rs.next())
+ continue;
+ ChunkSource cs = new ChunkSource(sha1);
+ do {
+ cs.addFile(FileSystem.composeAbsolutePath(rs.getString("filepath")).getAbsolutePath(),
+ rs.getLong("startbyte"), rs.getInt("blocksize"));
+ } while (rs.next());
+ if (list == null) {
+ list = new ArrayList<>();
+ }
+ list.add(cs);
+ }
+ } catch (SQLException e) {
+ LOGGER.error("Query failed in DbImageBlock.getBlocksWithHash()", e);
+ throw e;
+ }
+ return list;
+ }
+
}
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/IncomingDataTransfer.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/IncomingDataTransfer.java
index bb4765c1..15e9485b 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/IncomingDataTransfer.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/IncomingDataTransfer.java
@@ -26,6 +26,7 @@ import org.openslx.bwlp.thrift.iface.ImageVersionWrite;
import org.openslx.bwlp.thrift.iface.TNotFoundException;
import org.openslx.bwlp.thrift.iface.TransferInformation;
import org.openslx.bwlp.thrift.iface.TransferState;
+import org.openslx.bwlp.thrift.iface.UploadOptions;
import org.openslx.bwlp.thrift.iface.UserInfo;
import org.openslx.filetransfer.Downloader;
import org.openslx.filetransfer.util.ChunkStatus;
@@ -82,7 +83,7 @@ public class IncomingDataTransfer extends IncomingTransferBase {
public IncomingDataTransfer(String uploadId, UserInfo owner, ImageDetailsRead image,
File destinationFile, long fileSize, List<byte[]> sha1Sums, byte[] machineDescription,
boolean repairUpload) throws FileNotFoundException {
- super(uploadId, destinationFile, fileSize, sha1Sums);
+ super(uploadId, destinationFile, fileSize, sha1Sums, StorageChunkSource.instance);
this.owner = repairUpload ? null : owner;
this.image = image;
this.machineDescription = machineDescription;
@@ -93,7 +94,7 @@ public class IncomingDataTransfer extends IncomingTransferBase {
public IncomingDataTransfer(ImagePublishData publishData, File tmpFile, TransferInformation transferInfo,
boolean repairUpload) throws FileNotFoundException {
super(UUID.randomUUID().toString(), tmpFile, publishData.fileSize,
- ThriftUtil.unwrapByteBufferList(transferInfo.blockHashes));
+ ThriftUtil.unwrapByteBufferList(transferInfo.blockHashes), StorageChunkSource.instance);
ImageDetailsRead idr = new ImageDetailsRead();
idr.setCreateTime(publishData.createTime);
idr.setDescription(publishData.description);
@@ -389,4 +390,16 @@ public class IncomingDataTransfer extends IncomingTransferBase {
return errorMessage;
}
+ /**
+ * Alter options of this upload. Returns new effective options.
+ */
+ public UploadOptions setOptions(UploadOptions options) {
+ if (options != null) {
+ if (options.isSetServerSideCopying()) {
+ super.enableServerSideCopying(options.serverSideCopying);
+ }
+ }
+ return new UploadOptions(super.isServerSideCopyingEnabled());
+ }
+
}
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/StorageChunkSource.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/StorageChunkSource.java
new file mode 100644
index 00000000..01652fec
--- /dev/null
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/StorageChunkSource.java
@@ -0,0 +1,22 @@
+package org.openslx.bwlp.sat.fileserv;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.openslx.bwlp.sat.database.mappers.DbImageBlock;
+import org.openslx.filetransfer.LocalChunkSource;
+
+public class StorageChunkSource implements LocalChunkSource {
+
+ public static final StorageChunkSource instance = new StorageChunkSource();
+
+ @Override
+ public List<ChunkSource> getCloneSources(List<byte[]> sums) {
+ try {
+ return DbImageBlock.getBlocksWithHash(sums);
+ } catch (SQLException e) {
+ }
+ return null;
+ }
+
+}
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java
index 050f598d..b544a708 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java
@@ -9,6 +9,7 @@ import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -33,17 +34,34 @@ import org.openslx.util.Util;
public class ImageValidCheck implements Runnable {
- public enum CheckResult {
+ public static enum CheckResult {
+
+ QUEUED(0),
+ NO_SUCH_JOB(0),
+ WAITING_FOR_STORE(1),
+ WORKING(2),
+ DONE(100),
+ VERSION_EXPIRED(100),
+ DATABASE_PATH_MISSING(100),
+ DATABASE_PATH_INVALID(100),
+ FILE_NOT_FOUND(100),
+ FILE_ACCESS_ERROR(100),
+ FILE_SIZE_MISMATCH(100),
+ FILE_CORRUPT(100),
+ UNKNOWN_VERSIONID(100),
+ INTERNAL_ERROR(100);
+ public final int stage;
+ private CheckResult(int stage) {
+ this.stage = stage;
+ }
+ }
+
+ public static enum SubmitResult {
+ QUEUED,
NULL_POINTER_EXCEPTION,
ALREADY_IN_PROGRESS,
TOO_MANY_QUEUED_JOBS,
- SUBMITTED,
REJECTED_BY_SCHEDULER,
- WORKING,
- DONE,
- FILE_NOT_FOUND,
- FILE_ACCESS_ERROR,
- OTHER_ERROR,
}
private static final Logger LOGGER = Logger.getLogger(ImageValidCheck.class);
@@ -57,9 +75,13 @@ public class ImageValidCheck implements Runnable {
private final String versionId;
private final boolean integrity;
+ private final boolean updateState;
+
+ /**
+ * Status of this check job. Must never be null.
+ */
+ private CheckResult result = CheckResult.QUEUED;
- // TODO: Set appropriately in various places; make it possible to query from RPC
- private CheckResult result = CheckResult.DONE;
// Hash checking
@@ -84,26 +106,31 @@ public class ImageValidCheck implements Runnable {
// End hash checking
- public static CheckResult check(String versionId, boolean integrity) {
+ public static SubmitResult check(String versionId, boolean integrity, boolean updateState) {
if (versionId == null)
- return CheckResult.NULL_POINTER_EXCEPTION;
+ return SubmitResult.NULL_POINTER_EXCEPTION;
synchronized (inProgress) {
+ synchronized (done) {
+ if (done.containsKey(versionId)) {
+ done.remove(versionId);
+ }
+ }
if (inProgress.containsKey(versionId))
- return CheckResult.ALREADY_IN_PROGRESS;
+ return SubmitResult.ALREADY_IN_PROGRESS;
if (inProgress.size() >= MAX_CONCURRENT_CHECKS) {
if (queue.size() > 1000) {
- return CheckResult.TOO_MANY_QUEUED_JOBS;
+ return SubmitResult.TOO_MANY_QUEUED_JOBS;
}
- queue.add(new ImageValidCheck(versionId, integrity));
- return CheckResult.SUBMITTED;
+ queue.add(new ImageValidCheck(versionId, integrity, updateState));
+ return SubmitResult.QUEUED;
}
- ImageValidCheck check = new ImageValidCheck(versionId, integrity);
+ ImageValidCheck check = new ImageValidCheck(versionId, integrity, updateState);
if (Maintenance.trySubmit(check)) {
inProgress.put(versionId, check);
- return CheckResult.SUBMITTED;
+ return SubmitResult.QUEUED;
}
}
- return CheckResult.REJECTED_BY_SCHEDULER;
+ return SubmitResult.REJECTED_BY_SCHEDULER;
}
public static void checkForWork() {
@@ -127,48 +154,132 @@ public class ImageValidCheck implements Runnable {
}
}
- private ImageValidCheck(String versionId, boolean integrity) {
+ /**
+ * Get current status of check for given versionId.
+ * Never returns null.
+ *
+ * @param versionId VERSIONSID DES IMAGES
+ * @return state/result
+ */
+ public static CheckResult getStatus(String versionId) {
+ ImageValidCheck i;
+ synchronized (inProgress) {
+ i = inProgress.get(versionId);
+ }
+ if (i != null)
+ return i.result;
+ synchronized (done) {
+ i = done.get(versionId);
+ }
+ if (i != null)
+ return i.result;
+ return CheckResult.NO_SUCH_JOB;
+ }
+
+ /**
+ * Get status/result of all known check jobs.
+ *
+ * @return MAP
+ */
+ public static Map<String, CheckResult> getAll() {
+ Map<String, CheckResult> res = new HashMap<>();
+ synchronized (inProgress) {
+ for (Entry<String, ImageValidCheck> i : inProgress.entrySet()) {
+ res.put(i.getKey(), i.getValue().result);
+ }
+ }
+ synchronized (done) {
+ for (Entry<String, ImageValidCheck> i : done.getImmutableSnapshot().entrySet()) {
+ res.put(i.getKey(), i.getValue().result);
+ }
+ }
+ return res;
+ }
+
+ //
+ // Instance
+ //
+
+ private ImageValidCheck(String versionId, boolean integrity, boolean updateState) {
this.versionId = versionId;
this.integrity = integrity;
+ this.updateState = updateState;
+ }
+
+ private void setState(CheckResult cr) {
+ if (cr.stage > result.stage) {
+ result = cr;
+ } else {
+ LOGGER.debug("Ingoring state update from " + result.name() + " to " + cr.name());
+ }
}
@Override
public void run() {
try {
+ setState(CheckResult.WAITING_FOR_STORE);
if (!FileSystem.waitForStorage()) {
LOGGER.warn("Will not check " + versionId + ": Storage not online");
+ setState(CheckResult.INTERNAL_ERROR);
return;
}
+ setState(CheckResult.WORKING);
LocalImageVersion imageVersion;
try {
imageVersion = DbImage.getLocalImageData(versionId);
- } catch (SQLException e) {
- return;
} catch (TNotFoundException e) {
LOGGER.warn("Cannot check validity of image version - not found: " + versionId);
+ setState(CheckResult.UNKNOWN_VERSIONID);
+ return;
+ } catch (Exception e) {
+ LOGGER.warn("Cannot get local image data", e);
+ setState(CheckResult.INTERNAL_ERROR);
return;
}
+ // Found image in DB
+ // Simple checks first
boolean valid = checkValid(imageVersion);
if (valid && integrity) {
+ // Check block hashes
try {
valid = checkBlockHashes(imageVersion);
} catch (IOException e) {
- result = CheckResult.FILE_ACCESS_ERROR;
+ LOGGER.warn("IO error for " + versionId, e);
+ setState(CheckResult.FILE_ACCESS_ERROR);
valid = false;
} catch (Exception e) {
- result = CheckResult.OTHER_ERROR;
+ LOGGER.warn("Cannot check block hashes of " + versionId, e);
+ setState(CheckResult.INTERNAL_ERROR);
}
}
- if (imageVersion.isValid == valid)
- return; // nothing changed
+ if (imageVersion.isValid == valid) {
+ // nothing changed
+ if (valid) {
+ setState(CheckResult.DONE);
+ }
+ return;
+ }
// Update
try {
- DbImage.markValid(valid, false, imageVersion);
+ if (updateState) {
+ DbImage.markValid(valid, false, imageVersion);
+ }
+ if (valid) {
+ setState(CheckResult.DONE);
+ }
} catch (SQLException e) {
+ setState(CheckResult.INTERNAL_ERROR);
}
} finally {
+ if (result == CheckResult.WORKING) {
+ setState(CheckResult.INTERNAL_ERROR);
+ }
+ ImageValidCheck ivc;
synchronized (inProgress) {
- inProgress.remove(this.versionId);
+ synchronized (done) {
+ ivc = inProgress.remove(this.versionId);
+ done.put(this.versionId, ivc);
+ }
}
checkForWork();
}
@@ -184,11 +295,12 @@ public class ImageValidCheck implements Runnable {
versionDetails = DbImage.getVersionDetails(versionId);
} catch (TNotFoundException e) {
LOGGER.warn("Cannot check hash of image version - not found: " + versionId);
+ setState(CheckResult.UNKNOWN_VERSIONID);
return false;
} catch (SQLException e) {
+ setState(CheckResult.INTERNAL_ERROR);
return false;
}
- // TODO
if (versionDetails.sha1sums == null || versionDetails.sha1sums.isEmpty()) {
LOGGER.info("Image does not have block hashes -- assuming ok");
return true;
@@ -216,24 +328,27 @@ public class ImageValidCheck implements Runnable {
hashChecker.queue(chunk, buffer, new HashCheckCallback() {
@Override
public void hashCheckDone(HashResult result, byte[] data, FileChunk chunk) {
- if (result == HashResult.FAILURE) {
- // Hashing failed, cannot tell whether OK or not :(
- } else {
- if (result == HashResult.INVALID) {
- fileOk.set(false);
- ((StandaloneFileChunk) chunk).overrideStatus(ChunkStatus.MISSING);
+ try {
+ if (result == HashResult.FAILURE) {
+ // Hashing failed, cannot tell whether OK or not :(
} else {
- // >:(
- ((StandaloneFileChunk) chunk).overrideStatus(ChunkStatus.COMPLETE);
- }
- try {
- // We don't know what the state was in DB before, so just fire updates
- DbImageBlock.asyncUpdate(imageVersion.imageVersionId, chunk);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
+ if (result == HashResult.INVALID) {
+ fileOk.set(false);
+ ((StandaloneFileChunk) chunk).overrideStatus(ChunkStatus.MISSING);
+ } else {
+ // >:(
+ ((StandaloneFileChunk) chunk).overrideStatus(ChunkStatus.COMPLETE);
+ }
+ try {
+ // We don't know what the state was in DB before, so just fire updates
+ DbImageBlock.asyncUpdate(imageVersion.imageVersionId, chunk);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
}
+ } finally {
+ sem.release();
}
- sem.release();
}
}, HashChecker.BLOCKING | HashChecker.CALC_HASH);
numChecked += 1;
@@ -245,7 +360,11 @@ public class ImageValidCheck implements Runnable {
}
// Wait until the last callback fired
sem.acquire(numChecked);
- return fileOk.get();
+ if (fileOk.get()) {
+ return true;
+ }
+ setState(CheckResult.FILE_CORRUPT);
+ return false;
}
/**
@@ -256,28 +375,34 @@ public class ImageValidCheck implements Runnable {
return false;
if (imageVersion.expireTime < Util.unixTime()) {
LOGGER.info(versionId + ": expired");
+ setState(CheckResult.VERSION_EXPIRED);
return false;
}
if (imageVersion.filePath == null || imageVersion.filePath.isEmpty()) {
LOGGER.info(versionId + ": DB does not contain a path");
+ setState(CheckResult.DATABASE_PATH_MISSING);
return false;
}
File path = FileSystem.composeAbsoluteImagePath(imageVersion);
if (path == null) {
LOGGER.info(versionId + ": path from DB is not valid");
+ setState(CheckResult.DATABASE_PATH_INVALID);
return false;
}
if (!path.exists()) {
LOGGER.info(versionId + ": File does not exist (" + path.getAbsolutePath() + ")");
+ setState(CheckResult.FILE_NOT_FOUND);
return false;
}
if (!path.canRead()) {
LOGGER.info(versionId + ": File exists but not readable (" + path.getAbsolutePath() + ")");
+ setState(CheckResult.FILE_ACCESS_ERROR);
return false;
}
if (path.length() != imageVersion.fileSize) {
LOGGER.info(versionId + ": File exists but has wrong size (expected: " + imageVersion.fileSize
+ ", found: " + path.length() + ")");
+ setState(CheckResult.FILE_SIZE_MISMATCH);
return false;
}
return true;
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java
index df127736..f97231f8 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/ServerHandler.java
@@ -64,10 +64,12 @@ import org.openslx.bwlp.thrift.iface.TNotFoundException;
import org.openslx.bwlp.thrift.iface.TTransferRejectedException;
import org.openslx.bwlp.thrift.iface.TransferInformation;
import org.openslx.bwlp.thrift.iface.TransferStatus;
+import org.openslx.bwlp.thrift.iface.UploadOptions;
import org.openslx.bwlp.thrift.iface.UserInfo;
import org.openslx.bwlp.thrift.iface.Virtualizer;
import org.openslx.bwlp.thrift.iface.WhoamiInfo;
import org.openslx.sat.thrift.version.Version;
+import org.openslx.thrifthelper.Comparators;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.ThriftUtil;
@@ -124,8 +126,9 @@ public class ServerHandler implements SatelliteServer.Iface {
}
@Override
- public void updateBlockHashes(String uploadToken, List<ByteBuffer> blockHashes)
+ public void updateBlockHashes(String uploadToken, List<ByteBuffer> blockHashes, String userToken)
throws TInvalidTokenException {
+ // TODO: Validate user token some time in the future
IncomingDataTransfer upload = fileServer.getUploadByToken(uploadToken);
if (upload == null)
throw new TInvalidTokenException();
@@ -134,6 +137,20 @@ public class ServerHandler implements SatelliteServer.Iface {
}
@Override
+ public UploadOptions setUploadOptions(String userToken, String uploadToken, UploadOptions options)
+ throws TAuthorizationException, TInvalidTokenException, TException {
+ IncomingDataTransfer upload = fileServer.getUploadByToken(uploadToken);
+ if (upload == null)
+ throw new TInvalidTokenException();
+ if (options == null) // Query only -- don't validate user
+ return upload.setOptions(null);
+ UserInfo user = SessionManager.getOrFail(userToken);
+ if (Comparators.user.compare(user, upload.getOwner()) != 0)
+ throw new TAuthorizationException(AuthorizationError.NO_PERMISSION, "This isn't your upload");
+ return upload.setOptions(options);
+ }
+
+ @Override
public void cancelUpload(String uploadToken) {
IncomingDataTransfer upload = fileServer.getUploadByToken(uploadToken);
if (upload != null) {
@@ -762,4 +779,5 @@ public class ServerHandler implements SatelliteServer.Iface {
"Database failure when setting the virtualizer config for '" + imageVersionId + "'.");
}
}
+
}
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/FileSystem.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/FileSystem.java
index fa72056a..95cbcfe5 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/FileSystem.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/FileSystem.java
@@ -31,7 +31,7 @@ public class FileSystem {
* Delete given file on the {@link QuickTimer} thread, preventing the
* calling thread from freezing/hanging on I/O problems.
*
- * @param file the file to delete
+ * @param sourceCandidates the file to delete
*/
public static void deleteAsync(final File... files) {
if (files == null || files.length == 0)
@@ -126,14 +126,32 @@ public class FileSystem {
* <code>null</code> if the path would not be valid
*/
public static File composeAbsoluteImagePath(LocalImageVersion localImageData) {
- String rel = localImageData.filePath == null ? null
- : FilenameUtils.normalize(localImageData.filePath);
- if (rel == null || rel.startsWith("/")) {
- LOGGER.warn("Invalid path for local image " + localImageData.imageVersionId + ": "
- + localImageData.filePath);
+ if (localImageData == null)
return null;
+ File path = composeAbsolutePath(localImageData.filePath);
+ if (path == null) {
+ LOGGER.warn("ImageVersionId is " + localImageData.imageVersionId);
}
- return new File(Configuration.getVmStoreBasePath(), rel);
+ return path;
+ }
+
+ /**
+ * Given a local relative path for an image, return a {@link File} object
+ * that holds the full path to the image file in the file system.
+ *
+ * @param relativePath
+ * @return {@link File} representing the physical image file, or
+ * <code>null</code> if the path would not be valid
+ */
+ public static File composeAbsolutePath(String relativePath) {
+ if (relativePath != null) {
+ relativePath = FilenameUtils.normalize(relativePath);
+ }
+ if (relativePath == null || relativePath.startsWith("/")) {
+ LOGGER.warn("Invalid path for local image: " + relativePath);
+ return null;
+ }
+ return new File(Configuration.getVmStoreBasePath(), relativePath);
}
private static long lastStorageFailLog = 0;
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java
index 8018e86a..0e47994a 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java
@@ -22,6 +22,8 @@ import org.openslx.bwlp.sat.mail.SmtpMailer.EncryptionMode;
import org.openslx.bwlp.sat.maintenance.DeleteOldImages;
import org.openslx.bwlp.sat.maintenance.ImageValidCheck;
import org.openslx.bwlp.sat.maintenance.ImageValidCheck.CheckResult;
+import org.openslx.bwlp.sat.maintenance.ImageValidCheck.SubmitResult;
+import org.openslx.util.Json;
import org.openslx.util.Util;
import fi.iki.elonen.NanoHTTPD;
@@ -36,9 +38,12 @@ public class WebRpc {
if (uri.equals("delete-images")) {
return deleteImages();
}
- if (uri.equals("check-image")) {
+ if (uri.equals("start-image-check")) {
return checkImage(params);
}
+ if (uri.equals("query-image-check")) {
+ return queryImageCheck(params);
+ }
if (uri.equals("reset-mail-templates")) {
return resetMailTemplates();
}
@@ -52,12 +57,28 @@ public class WebRpc {
private static Response checkImage(Map<String, String> params) {
String versionId = params.get("versionid");
- boolean checkHashes = Boolean.valueOf(params.get("hash"));
if (versionId == null)
return WebServer.badRequest("Missing versionid param");
- CheckResult res = ImageValidCheck.check(versionId, checkHashes);
- return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8",
- res.name());
+ versionId = versionId.toLowerCase();
+ boolean checkHashes = Boolean.valueOf(params.get("hash"));
+ boolean updateState = Boolean.valueOf(params.get("update"));
+ SubmitResult res = ImageValidCheck.check(versionId, checkHashes, updateState);
+ return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8", res.name());
+ }
+
+ private static Response queryImageCheck(Map<String, String> params) {
+ String versionId = params.get("versionid");
+ Map<String, CheckResult> result;
+ if (versionId == null) {
+ result = ImageValidCheck.getAll();
+ } else {
+ versionId = versionId.toLowerCase();
+ CheckResult res = ImageValidCheck.getStatus(versionId);
+ result = new HashMap<>();
+ result.put(versionId, res);
+ }
+ return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "application/json; charset=utf-8",
+ Json.serialize(result));
}
/**