diff options
Diffstat (limited to 'dozentenmodulserver/src/main/java')
4 files changed, 144 insertions, 12 deletions
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java index 36c3a466..da52a5b3 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java @@ -1097,4 +1097,22 @@ public class DbImage { } } + /** + * Get all known file names of images, regardless of whether they are working/valid. + */ + public static Set<String> getAllFilenames() throws SQLException { + try (MysqlConnection connection = Database.getConnection()) { + MysqlStatement stmt = connection.prepareStatement("SELECT filepath FROM imageversion"); + ResultSet rs = stmt.executeQuery(); + Set<String> result = new HashSet<>(); + while (rs.next()) { + result.add(rs.getString("filepath")); + } + return result; + } catch (SQLException e) { + LOGGER.error("Query failed in DbImage.getAllFilenames()", e); + throw e; + } + } + } diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java index 4ecb9d99..c07a0ed9 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java @@ -30,7 +30,6 @@ import org.openslx.bwlp.thrift.iface.TNotFoundException; import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.util.Json; import org.openslx.util.Util; -import org.openslx.util.vm.UnsupportedVirtualizerFormatException; import org.openslx.util.vm.VmMetaData; import org.openslx.util.vm.VmMetaData.UsbSpeed; @@ -553,7 +552,7 @@ public class DbLecture { } public static LaunchData getClientLaunchData(String lectureId) throws SQLException, - TNotFoundException, UnsupportedVirtualizerFormatException { + TNotFoundException { LaunchData retval = new LaunchData(); byte[] config; String lectureName; 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 0e47994a..3a6f39ad 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 @@ -5,16 +5,25 @@ import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; +import java.sql.SQLException; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import javax.security.auth.login.LoginException; import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.log4j.Logger; import org.openslx.bwlp.sat.database.mappers.DbConfiguration; +import org.openslx.bwlp.sat.database.mappers.DbImage; import org.openslx.bwlp.sat.mail.MailTemplate; import org.openslx.bwlp.sat.mail.MailTemplatePlain.Template; import org.openslx.bwlp.sat.mail.SmtpMailer; @@ -23,6 +32,8 @@ 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.bwlp.sat.util.Configuration; +import org.openslx.bwlp.sat.util.FileSystem; import org.openslx.util.Json; import org.openslx.util.Util; @@ -31,6 +42,8 @@ import fi.iki.elonen.NanoHTTPD.Response; public class WebRpc { + private static final Logger LOGGER = Logger.getLogger(WebRpc.class); + public static Response handle(String uri, Map<String, String> params) { if (uri.equals("mailtest")) { return mailTest(params); @@ -47,6 +60,9 @@ public class WebRpc { if (uri.equals("reset-mail-templates")) { return resetMailTemplates(); } + if (uri.equals("scan-orphaned-files")) { + return scanForOrphanedFiles(params); + } return WebServer.notFound(); } @@ -55,6 +71,102 @@ public class WebRpc { return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8", "OK"); } + /** + * Scan the vmstore for orphaned files and images, return a list. + * If POST param 'action' is 'delete', all those files will be deleted. + */ + private static Response scanForOrphanedFiles(Map<String, String> params) { + if (!FileSystem.isStorageMounted()) + return WebServer.internalServerError("VMstore not mounted"); + final Map<String, DeleteResult> orphanedFiles = new HashMap<>(); + final String baseDir = Configuration.getVmStoreBasePath().toString(); + final int baseLen = baseDir.length() + (baseDir.endsWith("/") ? 0 : 1); + final boolean del = params.containsKey("action") && params.get("action").equals("delete"); + final Set<String> known; // These we want to keep + try { + known = DbImage.getAllFilenames(); + } catch (SQLException e1) { + return WebServer.internalServerError("Cannot query list of known images from database"); + } + if (known.isEmpty()) { + return WebServer.internalServerError("SAFTY CHECK: Known image list empty, aborting"); + } + AtomicInteger matches = new AtomicInteger(); + try { + // Consider only regular files, call checkFile for each one + Files.find(Configuration.getVmStoreProdPath().toPath(), 8, + (filePath, fileAttr) -> fileAttr.isRegularFile()) + .forEach((fileName) -> checkFile(fileName, orphanedFiles, baseLen, known, matches)); + } catch (IOException e) { + return WebServer.internalServerError(e.toString()); + } + if (del) { + for (Entry<String, DeleteResult> it : orphanedFiles.entrySet()) { + if (matches.get() == 0) { + // Don't delete anything if the set of known files and the set of files we actually + // found are disjoint + it.setValue(DeleteResult.SAFETY_ABORT); + continue; + } + Path filePath = Paths.get(baseDir + "/" + it.getKey()); + try { + Files.delete(filePath); + it.setValue(DeleteResult.DELETED); + } catch (Exception e) { + LOGGER.warn("Cannot delete " + filePath, e); + it.setValue(DeleteResult.ERROR); + } + } + } + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "application/json; charset=utf-8", + Json.serialize(orphanedFiles)); + } + + private static enum DeleteResult { + EXISTS, + DELETED, + ERROR, + SAFETY_ABORT; + } + + /** + * Function called for each file found on the VMstore to determine if it's + * orphaned. + * + * @param filePath File to check + * @param result Map to add the check result to + * @param baseLen length of the base path we need to strip from the absolute + * path + * @param known list of known images from the db + * @param matches counter for files that match a DB entry + */ + private static void checkFile(Path filePath, Map<String, DeleteResult> result, int baseLen, + Set<String> known, AtomicInteger matches) { + if (filePath.endsWith("dozmod.lock")) + return; + final String relativeFileName; + try { + relativeFileName = filePath.toAbsolutePath().toString().substring(baseLen); + } catch (IndexOutOfBoundsException e) { + LOGGER.warn("Cannot make image path relative", e); + return; + } + // Handle special dnbd3 files + String compareFileName; + if (relativeFileName.endsWith(".crc") || relativeFileName.endsWith(".map")) { + compareFileName = relativeFileName.substring(0, relativeFileName.length() - 4); + } else if (relativeFileName.endsWith(".meta")) { + compareFileName = relativeFileName.substring(0, relativeFileName.length() - 5); + } else { + compareFileName = relativeFileName; + } + if (known.contains(compareFileName)) { + matches.incrementAndGet(); + } else { + result.put(relativeFileName, DeleteResult.EXISTS); + } + } + private static Response checkImage(Map<String, String> params) { String versionId = params.get("versionid"); if (versionId == null) @@ -88,7 +200,8 @@ public class WebRpc { StringBuilder res = DeleteOldImages.hardDeleteImages(); if (res == null) return WebServer.internalServerError(); - return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8", res.toString()); + return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8", + res.toString()); } /** @@ -132,11 +245,9 @@ public class WebRpc { } smtpc = null; } - boolean ret = false; - if (smtpc != null) { - + if (smtpc != null) { MailTemplate template = DbConfiguration.getMailTemplate(Template.TEST_MAIL); Map<String, String> templateArgs = new HashMap<>(); templateArgs.put("host", host); @@ -149,8 +260,8 @@ public class WebRpc { ret = smtpc.send(recipient, "bwLehrpool Mail Test", msg, "<sat.bwlehrpool.de>"); } try { - baos.write(("\n\n-----------------------------------------\nTestergebnis: " + (ret ? "" : "nicht ") - + "erfolgreich").getBytes(StandardCharsets.UTF_8)); + baos.write(("\n\n-----------------------------------------\nTestergebnis: " + + (ret ? "" : "nicht ") + "erfolgreich").getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { } return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8", diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java index 3e91cfc5..6357e411 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java @@ -31,7 +31,6 @@ import org.openslx.bwlp.thrift.iface.TNotFoundException; import org.openslx.util.GrowingThreadPoolExecutor; import org.openslx.util.Json; import org.openslx.util.Util; -import org.openslx.util.vm.UnsupportedVirtualizerFormatException; import org.simpleframework.xml.Serializer; import org.simpleframework.xml.core.Persister; @@ -167,7 +166,7 @@ public class WebServer extends NanoHTTPD { final LaunchData ld; try { ld = DbLecture.getClientLaunchData(lectureId); - } catch (UnsupportedVirtualizerFormatException | TNotFoundException e) { + } catch (TNotFoundException e) { // TODO better virt error handling return notFound(); } catch (SQLException e) { @@ -288,10 +287,15 @@ public class WebServer extends NanoHTTPD { /** * Helper for returning "Internal Server Error" Status + * @param body Message */ - public static Response internalServerError() { + public static Response internalServerError(String body) { return new NanoHTTPD.Response(NanoHTTPD.Response.Status.INTERNAL_ERROR, "text/plain", - "Internal Server Error"); + body); + } + + public static Response internalServerError() { + return internalServerError("Internal Server Error"); } /** |