package org.openslx.dozmod.gui.wizard.page;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.ImageDetailsRead;
import org.openslx.bwlp.thrift.iface.Virtualizer;
import org.openslx.dozmod.Branding;
import org.openslx.dozmod.Config;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.helper.MessageType;
import org.openslx.dozmod.gui.helper.QFileChooser;
import org.openslx.dozmod.gui.wizard.Wizard;
import org.openslx.dozmod.gui.wizard.layout.ImageUploadPageLayout;
import org.openslx.dozmod.state.UploadWizardState;
import org.openslx.dozmod.thrift.Session;
import org.openslx.dozmod.thrift.ThriftActions;
import org.openslx.dozmod.thrift.ThriftError;
import org.openslx.dozmod.thrift.UploadInitiator;
import org.openslx.dozmod.thrift.WrappedException;
import org.openslx.dozmod.thrift.cache.MetaDataCache;
import org.openslx.sat.thrift.version.Feature;
import org.openslx.thrifthelper.TConst;
import org.openslx.util.vm.DiskImage;
import org.openslx.util.vm.DiskImage.UnknownImageFormatException;
import org.openslx.util.vm.VmMetaData;
import org.openslx.util.vm.VmMetaData.HardDisk;
import org.openslx.util.vm.VmwareMetaData;
/**
* Page for uploading a new image.
*/
@SuppressWarnings("serial")
public class ImageUploadPage extends ImageUploadPageLayout {
private final static Logger LOGGER = Logger.getLogger(ImageUploadPage.class);
private UploadWizardState state;
private String lastDetectedName = null;
private ImageDetailsRead existingImage = null;
private final FileNameExtensionFilter allSupportedFilter;
private final FileNameExtensionFilter vmxFilter = new FileNameExtensionFilter("VMware Virtual Machine",
"vmx");
private final FileNameExtensionFilter vboxFilter = new FileNameExtensionFilter(
"VirtualBox Virtual Machine", "vbox");
public ImageUploadPage(Wizard wizard, UploadWizardState uploadWizardState,
final ImageDetailsRead existingImage) {
super(wizard);
setPageComplete(false);
this.canComeBack = false;
this.state = uploadWizardState;
this.existingImage = existingImage;
lblImageName.setVisible(existingImage == null);
txtImageName.setVisible(existingImage == null);
txtInfoText.setVisible(existingImage == null);
// show the licensed software checkbox since we are uploading new version
chkLicenseRestricted.setVisible(existingImage != null);
chkLicenseRestricted.setSelected(existingImage != null); // TODO selected by default?
// Browse for *.vmx
btnBrowseForImage.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
browseForVm();
}
});
txtImageFile.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 1)
browseForVm();
}
});
// initialize allSupportedFilter depending on whether multiple hypervisors
// are supported or not
if (Session.hasFeature(Feature.MULTIPLE_HYPERVISORS)) {
allSupportedFilter = new FileNameExtensionFilter("All Supported", "vmx", "vbox");
} else {
allSupportedFilter = new FileNameExtensionFilter("All Supported", "vmx");
}
btnBrowseForImage.requestFocus();
}
private void browseForVm() {
QFileChooser fc = new QFileChooser(Config.getUploadPath(), false);
fc.setAcceptAllFileFilterUsed(false);
fc.addChoosableFileFilter(vmxFilter);
if (Session.hasFeature(Feature.MULTIPLE_HYPERVISORS)) {
fc.addChoosableFileFilter(vboxFilter);
}
fc.addChoosableFileFilter(allSupportedFilter);
fc.setFileFilter(allSupportedFilter);
// differentiate between existing and new VMs
if (existingImage != null) {
if (existingImage.virtId.equals(TConst.VIRT_VMWARE)) {
fc.setFileFilter(vmxFilter);
} else if (existingImage.virtId.equals(TConst.VIRT_VIRTUALBOX)) {
fc.setFileFilter(vboxFilter);
}
}
int action = fc.showOpenDialog(getDialog());
File file = fc.getSelectedFile();
if (action != JFileChooser.APPROVE_OPTION || file == null)
return;
vmSelected(file.getAbsoluteFile());
}
private void vmSelected(File file) {
Config.setUploadPath(file.getParent());
txtImageFile.setText("");
txtImageName.setText("");
try {
// gets the metadata object of the selected VM depending on its type
state.meta = VmMetaData.getInstance(MetaDataCache.getOperatingSystems(), file);
} catch (IOException e) {
Gui.showMessageBox(this, "Konnte " + file.getPath() + " nicht lesen", MessageType.ERROR, LOGGER,
e);
setPageComplete(false);
return;
}
if (state.meta == null || state.meta.getDisplayName() == null) {
setErrorMessage("Ungültige Konfigurationsdatei ausgewählt!");
setPageComplete(false);
return;
}
final String fileformat = state.meta.getVirtualizer().virtName;
// bail if multiple hypervisors are not supported
if (!(state.meta instanceof VmwareMetaData) && !Session.hasFeature(Feature.MULTIPLE_HYPERVISORS)) {
setErrorMessage("Der Hypervisor der gewählten VM (" + fileformat
+ ") wird vom aktuellen Satellitenserver nicht unterstützt.");
setPageComplete(false);
return;
}
// check if the user somehow changed the type of the VM
if (existingImage != null && !existingImage.virtId.equals(state.meta.getVirtualizer().virtId)) {
Virtualizer existingImageVirtualizer = MetaDataCache.getVirtualizerById(existingImage.virtId);
String existingImageVirtualizerName = "<error>";
if (existingImageVirtualizer != null)
existingImageVirtualizerName = existingImageVirtualizer.virtName;
setErrorMessage("Neue Versionen müssen vom Typ " + existingImageVirtualizerName + " sein.");
setPageComplete(false);
return;
}
List<HardDisk> hdds = state.meta.getHdds();
if (hdds.size() == 0 || hdds.get(0).diskImage == null) {
setErrorMessage("Die gewählte " + fileformat + "-Datei enthält keine virtuelle Festplatte!");
setPageComplete(false);
return;
}
if (hdds.size() > 1) {
setErrorMessage(
"Die gewählte " + fileformat + "-Datei enthält mehr als eine virtuelle Festplatte!");
setPageComplete(false);
return;
// Allow to continue!?
}
// now check the disk files
File vmDiskFileInfo = new File(hdds.get(0).diskImage);
if (!vmDiskFileInfo.isAbsolute()) {
// it's relative, compose path using the vmx location
File vmBaseDirectory = file.getParentFile();
vmDiskFileInfo = new File(vmBaseDirectory, hdds.get(0).diskImage);
}
DiskImage diskImage;
try {
diskImage = new DiskImage(vmDiskFileInfo);
} catch (FileNotFoundException e) {
setErrorMessage("'" + vmDiskFileInfo.getName() + "' kann nicht gefunden werden!");
setPageComplete(false);
return;
} catch (IOException e) {
setErrorMessage("'" + vmDiskFileInfo.getName() + "' kann nicht gelesen werden!");
setPageComplete(false);
return;
} catch (UnknownImageFormatException e) {
setErrorMessage("'" + vmDiskFileInfo.getName() + "' hat unbekanntes Dateiformat!");
LOGGER.debug("Selected disk file has unknown format.", e);
setPageComplete(false);
return;
}
// Warn user about snapshot
if (diskImage.isSnapshot || state.meta.isMachineSnapshot()) {
Gui.showMessageBox("Von der ausgewählten VM wurde ein Snapshot erstellt. In diesem Zustand kann\n"
+ "die VM leider nicht ins " + Branding.getServiceName() + "-System geladen werden. Bitte konsolidieren Sie zunächst\n"
+ "den Snapshot und versuchen Sie es erneut.", MessageType.WARNING, null, null);
setErrorMessage("Die gewählte VM befindet sich im Snapshot-Zustand.");
setPageComplete(false);
return;
}
if (!diskImage.isStandalone) {
Gui.showMessageBox("Die zu dieser VM gehörige Virtuelle Festplatte ist im Format '"
+ diskImage.subFormat + "'.\n"
+ "Dieses Format wird von " + Branding.getApplicationName() + " nicht unterstützt. Bitte konvertieren Sie die VM\n"
+ "in das Format 'monolithicSparse'.", MessageType.WARNING, null, null);
setErrorMessage("Die VMDK Datei der VM hat ein inkompatibles Format");
setPageComplete(false);
return;
}
// everything seems fine so far
state.diskFile = vmDiskFileInfo;
state.descriptionFile = file;
if (existingImage == null) {
// User didn't enter a name yet or didn't change it -> set
String imageName = txtImageName.getText();
if (imageName.isEmpty() || imageName.equals(lastDetectedName)) {
txtImageName.setText(state.meta.getDisplayName());
}
}
lastDetectedName = state.meta.getDisplayName();
state.detectedOs = state.meta.getOs();
txtImageFile.setText(file.getAbsolutePath());
// let the user know the upload is ready
setErrorMessage(null);
setDescription("Sie können jetzt den Upload starten.");
setPageComplete(true);
}
private boolean askCancelLockFile(String... lockFiles) {
for (String lockFile : lockFiles) {
File file = new File(lockFile);
if (!file.exists())
continue;
return !Gui.showMessageBox(this,
"Die gewählte VM scheint noch in Verwendung zu sein. Bitte fahren Sie die VM\n"
+ "vor dem Hochladen herunter und schließen Sie den VMware Player, ansonsten\n"
+ "kann die VM nach dem Hochladen beschädigt sein.\n\n"
+ "Möchten Sie diese Warnung ignorieren und trotzdem fortfahren?\n"
+ "(Sie sollten sich sicher sein, was sie tun, wenn Sie hier 'Ja' wählen)",
MessageType.QUESTION_YESNO, null, null);
}
return false;
}
/**
* This function starts the image creation process. It is triggered by the
* "Next" button.
*
* Depending on the state, it will first try to get a UUID for the new image
* by calling createImage() of the thrift API. If a UUID is received, it
* will request an upload with requestImageVersionUpload(). If granted, it
* will finally start a thread for the UploadTask.
*
* Then a callback to the Gui is executed where we can process the upload
* state and give the user feedback about it.
*
*/
@Override
protected boolean wantNextOrFinish() {
// Check for vmware player lock files - warn user if found, might corrupt upload
if (askCancelLockFile(state.descriptionFile.getAbsolutePath() + ".lck",
state.diskFile.getAbsolutePath() + ".lck")) {
setErrorMessage("Die gewählte VM wird noch verwendet");
return false;
}
// are we creating a new image? then either:
// get the image name either auto filled by VmwareMetaData or by user
// get the image name from the image we are uploading a new version of
state.name = existingImage != null ? existingImage.getImageName() : txtImageName.getText();
// -- create image to get uuid --
if (existingImage == null) {
if (state.uuid == null) {
state.uuid = ThriftActions.createImage(JOptionPane.getFrameForComponent(this), state.name);
if (state.uuid == null)
return false;
txtImageName.setEnabled(false);
btnBrowseForImage.setEnabled(false);
txtImageFile.setEnabled(false);
}
} else {
state.uuid = existingImage.getImageBaseId();
}
// Create upload initiator that will manage requesting a token, hashing the
// file, connecting for upload...
if (state.upload == null) {
try {
state.upload = new UploadInitiator(state.uuid, state.diskFile,
state.meta.getFilteredDefinition());
} catch (WrappedException e) {
ThriftError.showMessage(this, LOGGER, e.exception, e.displayMessage);
return false;
} catch (IOException e) {
Gui.showMessageBox(this, "Upload-Initialisierung fehlgeschlagen", MessageType.ERROR, LOGGER,
e);
return false;
}
}
// Start the hash check now
state.upload.startHashing();
return true;
}
}