summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/thrift/ThriftActions.java
blob: 8b8ceb907ba620fd22489c3cafe0edfc76409827 (plain) (tree)
1
2
3
4
5
6
7
8
9





                                     
                           
                                              
                      




                                    


                                                             

                                                         
                                                          
                                                    
                                                  






                                                  
                             





                                                                                   
                                                                                                
           
                                                             











                                                                                          
                       




































                                                                                                                          



















                                                                                                                                                                                                                    




                                                                                              















































                                                                                                                                              


















































































                                                                                                                  
                                              











                                                                                                           
                                                  







                                                                                                                       
















                                                                                                                                          

         
           

                              




                                     
                                                                
                                                                                     

                                                                                                    
                                                  

                                                     
                                                                                         


                                                                      
                                                                                                               


                                                                                                
           
                                                                
          
                                           

                                                                                          













                                                                               



                                                                                                





                                                                                                            
                                                                                                
                                                                                     







                                                                                                                                   
                               

                                       




                                                    








                                                                                                                                             

                                                        
                                                                                                             













                                                                                    










                                                                                                    
 
package org.openslx.dozmod.thrift;

import java.awt.Frame;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.util.List;

import javax.swing.JFileChooser;

import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.openslx.bwlp.thrift.iface.ImageVersionWrite;
import org.openslx.bwlp.thrift.iface.TAuthorizationException;
import org.openslx.bwlp.thrift.iface.TImageDataException;
import org.openslx.bwlp.thrift.iface.TransferInformation;
import org.openslx.dozmod.Config;
import org.openslx.dozmod.filetransfer.AsyncHashGenerator;
import org.openslx.dozmod.filetransfer.DownloadTask;
import org.openslx.dozmod.filetransfer.UploadTask;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.Gui.GuiCallable;
import org.openslx.dozmod.gui.MainWindow;
import org.openslx.dozmod.gui.helper.MessageType;
import org.openslx.dozmod.gui.helper.QFileChooser;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.QuickTimer;
import org.openslx.util.Util;
import org.openslx.util.QuickTimer.Task;

public class ThriftActions {

	private static final Logger LOGGER = Logger.getLogger(ThriftActions.class);

	/* *******************************************************************************	
	 * 
	 *                             IMAGE VERSION DOWNLOAD
	 *
	 * Download image version action composed of the interface 'DownloadCallback'
	 * and the actual static method 'initDownload' to start the download.
	 * 
	 ********************************************************************************/
	/**
	 * The callback interface to inform the GUI about the status of the operation 
	 */
	public interface DownloadCallback {
		void downloadInitialized(boolean success);
	}
	/**
	 * NON-BLOCKING
	 * Initializes the download of the given imageVersionId saving it to the given
	 * imageName
	 * 
	 * @param frame caller of this method
	 * @param imageVersionId image version id to download
	 * @param imageName destination file name
	 * @param virtualizerId id of the virtualizer
	 * @param imageSize size in bytes of the image to be downloaded
	 * @param callback callback function to return status of this operation to the GUI 
	 */
	public static void initDownload(final Frame frame, final String imageVersionId, final String imageName,
			final String virtualizerId, final long imageSize, final DownloadCallback callback) {
		QFileChooser fc = new QFileChooser(Config.getDownloadPath(), true);
		fc.setDialogTitle("Bitte wählen Sie einen Speicherort");
		int action = fc.showSaveDialog(frame);
		final File file = fc.getSelectedFile();
		if (action != JFileChooser.APPROVE_OPTION || file == null)
			return;

		QuickTimer.scheduleOnce(new Task() {
			@Override
			public void fire() {
				final TransferInformation transInf;
				try {
					transInf = ThriftManager.getSatClient()
							.requestDownload(Session.getSatelliteToken(), imageVersionId);
				} catch (TException e) {
					ThriftError.showMessage(frame, LOGGER, e, "Die Download-Anfrage ist gescheitert");
					if (callback != null)
						callback.downloadInitialized(false);
					return;
				}

				File df = null;
				try {
					file.getAbsoluteFile().mkdirs();
					df = new File(file.getAbsolutePath(), generateFilename(imageName, virtualizerId));
					final File ff = df;
					if (df.exists()) {
						if (!Gui.syncExec(new GuiCallable<Boolean>() {
							@Override
							public Boolean run() {
								return Gui.showMessageBox(frame,
										"Datei '" + ff.getAbsolutePath() + "' existiert bereits, wollen Sie sie überschreiben?", MessageType.QUESTION_YESNO, LOGGER, null);
							}
						})) {
							// user aborted
							return;
						} else {
							// delete it
							if (!df.delete()) {
								// TODO what?
								Gui.showMessageBox(frame, "Datei konnte nicht überschrieben werden!", MessageType.ERROR, LOGGER, null);
								return;
							}
						}
					}
					df.createNewFile();
				} catch (IOException e) {
					LOGGER.warn("Cannot prepare download destination", e);
				}
				final File destFile = df;
				final DownloadTask dlTask;
				try {
					dlTask = new DownloadTask(Session.getSatelliteAddress(), transInf.getPlainPort(), transInf
							.getToken(), destFile, imageSize, null);
				} catch (final FileNotFoundException e) {
					Gui.asyncMessageBox("Konnte Download nicht vorbereiten: Der gewählte Zielort ist nicht beschreibbar",
							MessageType.ERROR, LOGGER, e);
					if (callback != null)
						callback.downloadInitialized(false);
					return;
				}
				new Thread(dlTask).start();

				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						Config.setDownloadPath(file.getAbsolutePath());
						MainWindow.addDownload(imageName, destFile.getName(), dlTask);
						if (callback != null)
							callback.downloadInitialized(true);
					}
				});
			}
		});
	}
	/**
	 * Generates a filename based on the given imageName and with the proper extension
	 * depending on the virtualizer
	 * 
	 * @param imageName
	 * @param virtualizerId
	 * @return the generated name as String
	 */
	private static String generateFilename(String imageName, String virtualizerId) {
		String fileName = imageName.replaceAll("[^a-zA-Z0-9_\\.\\-]+", "_");
		if (fileName.length() > 50) {
			fileName = fileName.substring(0, 50);
		}
		if ("vmware".equals(virtualizerId)) {
			fileName += ".vmdk";
		} else if ("virtualbox".equals(virtualizerId)) {
			fileName += ".vdi";
		} else {
			fileName += ".img";
		}
		return fileName;
	}

	/* *******************************************************************************	
	 * 
	 *                              IMAGE CREATION
	 *
	 * Creates a base image with the given name
	 * 
	 ********************************************************************************/
	/**
	 * GUI-BLOCKING
	 * Creates the image with the given name. Returns the uuid returned by the server
	 * 
	 * @param frame calling this action
	 * @return uuid as String, or null if the creation failed
	 */
	public static String createImage(final Frame frame, final String name) {
		String uuid = null;
		try {
			uuid = ThriftManager.getSatClient().createImage(Session.getSatelliteToken(), name);
		} catch (TAuthorizationException e) {
			Gui.showMessageBox(frame, "Sie sind nicht Berechtigt ein Image zu estellen.",
					MessageType.ERROR, LOGGER, e);
		} catch (TImageDataException e) {
			Gui.showMessageBox(frame, "Ungültige Angaben (" + e.getNumber() + ")\n" + e.getMessage(),
					MessageType.ERROR, LOGGER, e);
		} catch (Exception e) {
			Gui.showMessageBox(frame, "Unbekannter Fehler beim Erstellen der VM", MessageType.ERROR,
					LOGGER, e);
		}
		return uuid;
	}

	/* *******************************************************************************	
	 * 
	 *                              IMAGE VERSION UPLOAD
	 *
	 * Methods to upload an image version. This is compromised of two distinct steps:
	 *  - requestVersionUpload(..) to request the upload at the server
	 *  - initUpload(..) to actually start the upload of the file
	 * 
	 ********************************************************************************/
	/**
	 * GUI-BLOCKING
	 * Request the upload of an image version. Returns the TransferInformation received by the server
	 * or null if the request failed. Will give user feedback about failures.
	 * 
	 * @param frame caller of this method
	 * @param imageBaseId uuid of the image to upload a version of
	 * @param fileSize size in bytes(?) of the uploaded file
	 * @param blockHashes
	 * @param machineDescription
	 * @param callback
	 * @return TransferInformation received by the server, null if the request failed.
	 */
	public static TransferInformation requestVersionUpload(final Frame frame, final String imageBaseId,
			final long fileSize, final List<ByteBuffer> blockHashes,
			final ByteBuffer machineDescription) {
		TransferInformation ti = null;
		try {
			ti = ThriftManager.getSatClient().requestImageVersionUpload(
					Session.getSatelliteToken(),
					imageBaseId,
					fileSize,
					null, // TODO remove deprecated parameter
					machineDescription);
			LOGGER.info("Version upload granted, versionId: '" + ti.toString());
		} catch (TAuthorizationException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Upload einer neuen Version nicht erlaubt!");
		} catch (TException e) {
			ThriftError.showMessage(frame, LOGGER, e, "Upload-Anfrage gescheitert!");
		}
		return ti;
	}
	/**
	 * GUI-BLOCKING
	 * Starts uploading the given diskFile using the transferInformation and hashGen
	 *  
	 * @param frame caller of this method
	 * @param transferInformation transfer information to use for the upload
	 * @param hashGen hash generator for this file
	 * @param diskFile the file to upload
	 * @return UploadTask if the uploading initialized, or null if uploading failed
	 */
	public static UploadTask initUpload(final Frame frame, final TransferInformation transferInformation,
			final File diskFile) {
		UploadTask uploadTask = null;
		// do actually start the upload now
		LOGGER.debug("Starting upload for: " + diskFile.toPath());
		try {
			uploadTask = new UploadTask(Session.getSatelliteAddress(),
					transferInformation.getPlainPort(), transferInformation.getToken(),
					diskFile);
		} catch (FileNotFoundException e) {
			Gui.asyncMessageBox("Kann VM nicht hochladen: Datei nicht gefunden\n\n"
					+ diskFile.getAbsolutePath(), MessageType.ERROR, LOGGER, e);
			return null;
		}
		AsyncHashGenerator hashGen = null;
		try {
			hashGen = new AsyncHashGenerator(transferInformation.token, diskFile);
			hashGen.start();
			Util.sleep(50); // A little ugly... Give the hash generator a head start
		} catch (FileNotFoundException | NoSuchAlgorithmException e) {
			Gui.showMessageBox(frame, "Kann keine Block-Hashes für den Upload berechnen, "
					+ "automatische Fehlerkorrektur deaktiviert.", MessageType.WARNING, LOGGER, e);
		}
		Util.sleep(50); // A little ugly... Give the hash generator a head start
		Thread uploadThread = new Thread(uploadTask);
		uploadThread.setDaemon(true);
		uploadThread.start();
		do { // Even more ugly - block the GUI thread so we know whether the upload started, and only then switch to the next page
			Util.sleep(5);
		} while (uploadTask.getFailCount() == 0 && uploadTask.getTransferCount() == 0
				&& !uploadTask.isCanceled());

		if (uploadTask.getTransferCount() == 0) {
			Gui.asyncMessageBox("Aufbau der Verbindung zum Hochladen fehlgeschlagen", MessageType.ERROR,
					LOGGER, null);
			hashGen.cancel();
			uploadTask.cancel();
			uploadTask = null;
		}
		return uploadTask;
	}
	
	/**
	 * GUI-BLOCKING
	 * Gives user feedback
	 * TODO
	 * @param frame
	 * @param transferInformation
	 * @param versionInfo
	 */
	public static void updateImageVersion(final Frame frame,
			final String versionId, final ImageVersionWrite versionInfo){
		try  {
			ThriftManager.getSatClient().updateImageVersion(Session.getSatelliteToken(),
					versionId,
					versionInfo);
		} catch (TException e) {
			Gui.showMessageBox(frame, "Konnte neue Version nicht erstellen!",
					MessageType.ERROR, LOGGER, e);
			return;
		}
		Gui.showMessageBox(frame, "Neue Version erfolgreich erstellt", MessageType.INFO, LOGGER, null);
	}
	
	/* *******************************************************************************	
	 * 
	 *                              IMAGE / VERSION DELETION
	 *
	 * Deletes a specific image version
	 * 
	 ********************************************************************************/
	/**
	 * Delete callback interface to be implemented by callers of 
	 * ThriftActions.deleteImageVersion(..)
	 */
	public interface DeleteCallback {
		/**
		 * Called once the status of a delete operation is determined
		 * 
		 * @param success true if deleted successfully, false otherwise
		 */
		void isDeleted(boolean success);
	}
	/**
	 * NON-BLOCKING
	 * Deletes either an image base or an image version depending on the parameters. 
	 * To delete an image base, give the imageBaseId and let imageVersionId be null.
	 * To delete an image version, set both imageBaseId and imageVersionId.
	 * The success of the operation will be forwarded to the GUI through the DeleteCallback.
	 * 
	 * @param frame next parent frame of the caller of this method
	 * @param imageBaseId uuid of the image that belongs to the version
	 * @param imageVersionId id of the image version to be deleted
	 * @param callback called to inform the GUI about the deletion status (see DeleteCallback interface)
	 */
	public static void deleteImageBaseOrVersion(final Frame frame, final String imageBaseId,
			final String imageVersionId, final DeleteCallback callback) {
		String questionText;
		if (imageBaseId == null) {
			return;
		} else {
			questionText = imageVersionId == null ?
				"Wollen Sie dieses Image wirklich löschen?" : "Wollen Sie diese Image-Version wirklich löschen?";
		}
		if (!userConfirmed(frame, questionText))
			return;

		// perform the deletion
		QuickTimer.scheduleOnce(new Task() {
			boolean success = false;
			@Override
			public void fire() {
				try {
					if (imageVersionId == null) {
						// deleting an image base
						ThriftManager.getSatClient().deleteImageBase(Session.getSatelliteToken(), imageBaseId);
						LOGGER.info("Deleted image with id '" + imageBaseId + "'");
					} else {
						// deleting an image version
						ThriftManager.getSatClient().deleteImageVersion(Session.getSatelliteToken(), imageVersionId);
						LOGGER.info("Deleted version '" + imageVersionId + "' of image '" + imageBaseId + "'.");
					}
					success = true;
				} catch (TException e) {
					ThriftError.showMessage(frame, LOGGER, e, "Löschen fehlgeschlagen");
					if (callback != null)
						callback.isDeleted(success);
					return;
				}
				Gui.asyncExec(new Runnable() {
					@Override
					public void run() {
						if (callback != null)
							callback.isDeleted(success);
					}
				});
			}
		});
	}
	
	/** 
	 * Helper to ask the user for confirmation. Returns his choice.
	 * 
	 * @param frame frame to show this message box on
	 * @param message question message to display to the user
	 * @return true if the user confirmed (clicked yes), false otherwise
	 */
	private static boolean userConfirmed(final Frame frame, final String message) {
		return Gui.showMessageBox(frame, message, MessageType.QUESTION_YESNO, LOGGER, null);
	}
}