package org.ovirt.engine.ui.uicommonweb.models.vms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.ovirt.engine.core.common.action.CreateAllSnapshotsFromVmParameters;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.businessentities.BusinessEntitiesDefinitions;
import org.ovirt.engine.core.common.businessentities.Snapshot;
import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotStatus;
import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotType;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.comparators.DiskByDiskAliasComparator;
import org.ovirt.engine.core.common.businessentities.comparators.LexoNumericNameableComparator;
import org.ovirt.engine.core.common.businessentities.network.VmNetworkInterface;
import org.ovirt.engine.core.common.businessentities.storage.Disk;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.Version;
import org.ovirt.engine.ui.frontend.AsyncCallback;
import org.ovirt.engine.ui.frontend.Frontend;
import org.ovirt.engine.ui.uicommonweb.ICommandTarget;
import org.ovirt.engine.ui.uicommonweb.UICommand;
import org.ovirt.engine.ui.uicommonweb.dataprovider.AsyncDataProvider;
import org.ovirt.engine.ui.uicommonweb.help.HelpTag;
import org.ovirt.engine.ui.uicommonweb.models.EntityModel;
import org.ovirt.engine.ui.uicommonweb.models.ListModel;
import org.ovirt.engine.ui.uicommonweb.validation.IValidation;
import org.ovirt.engine.ui.uicommonweb.validation.LengthValidation;
import org.ovirt.engine.ui.uicommonweb.validation.NotEmptyValidation;
import org.ovirt.engine.ui.uicommonweb.validation.SpecialAsciiI18NOrNoneValidation;
import org.ovirt.engine.ui.uicompat.ConstantsManager;
import org.ovirt.engine.ui.uicompat.PropertyChangedEventArgs;
public class SnapshotModel extends EntityModel<Snapshot> {
private VM vm;
public VM getVm() {
return vm;
}
public void setVm(VM value) {
if (vm != value) {
vm = value;
onPropertyChanged(new PropertyChangedEventArgs("VM")); //$NON-NLS-1$
}
}
private ArrayList<DiskImage> disks;
public ArrayList<DiskImage> getDisks() {
return disks;
}
public void setDisks(ArrayList<DiskImage> value) {
if (disks != value) {
disks = value;
onPropertyChanged(new PropertyChangedEventArgs("Disks")); //$NON-NLS-1$
}
}
private List<DiskImage> vmDisks;
public List<DiskImage> getVmDisks() {
return vmDisks;
}
public void setVmDisks(List<DiskImage> value) {
if (vmDisks != value) {
vmDisks = value;
onPropertyChanged(new PropertyChangedEventArgs("VmDisks")); //$NON-NLS-1$
}
}
private List<VmNetworkInterface> nics;
public List<VmNetworkInterface> getNics() {
return nics;
}
public void setNics(List<VmNetworkInterface> value) {
if (nics != value) {
nics = value;
onPropertyChanged(new PropertyChangedEventArgs("Nics")); //$NON-NLS-1$
}
}
private List<String> apps;
public List<String> getApps() {
return apps;
}
public void setApps(List<String> value) {
if (apps != value) {
apps = value;
onPropertyChanged(new PropertyChangedEventArgs("Apps")); //$NON-NLS-1$
}
}
private EntityModel<String> privateDescription;
public EntityModel<String> getDescription() {
return privateDescription;
}
public void setDescription(EntityModel<String> value) {
privateDescription = value;
}
private EntityModel<Boolean> memory;
public EntityModel<Boolean> getMemory() {
return memory;
}
public void setMemory(EntityModel<Boolean> value) {
memory = value;
}
private boolean validateByVmSnapshots;
public boolean isValidateByVmSnapshots() {
return validateByVmSnapshots;
}
public void setValidateByVmSnapshots(boolean validateByVmSnapshots) {
this.validateByVmSnapshots = validateByVmSnapshots;
}
private ListModel<DiskImage> snapshotDisks;
public ListModel<DiskImage> getSnapshotDisks() {
return snapshotDisks;
}
public void setSnapshotDisks(ListModel<DiskImage> value) {
snapshotDisks = value;
}
private boolean showMemorySnapshotWarning;
public boolean isShowMemorySnapshotWarning() {
return showMemorySnapshotWarning;
}
public void setShowMemorySnapshotWarning(boolean value) {
showMemorySnapshotWarning = value;
}
private Version oldClusterVersionOfSnapshotWithMemory;
private boolean showPartialSnapshotWarning;
public boolean isShowPartialSnapshotWarning() {
return showPartialSnapshotWarning;
}
public void setShowPartialSnapshotWarning(boolean value) {
showPartialSnapshotWarning = value;
}
private ListModel<PreivewPartialSnapshotOption> partialPreviewSnapshotOptions;
public ListModel<PreivewPartialSnapshotOption> getPartialPreviewSnapshotOptions() {
return partialPreviewSnapshotOptions;
}
private void setPartialPreviewSnapshotOptions(ListModel<PreivewPartialSnapshotOption> value) {
partialPreviewSnapshotOptions = value;
}
private UICommand cancelCommand;
public UICommand getCancelCommand() {
return cancelCommand != null ? cancelCommand : super.getCancelCommand();
}
public void setCancelCommand(UICommand cancelCommand) {
this.cancelCommand = cancelCommand;
}
public SnapshotModel() {
setDescription(new EntityModel<String>());
setMemory(new EntityModel<>(true));
setDisks(new ArrayList<DiskImage>());
setNics(new ArrayList<VmNetworkInterface>());
setApps(new ArrayList<String>());
setSnapshotDisks(new ListModel<DiskImage>());
ListModel<PreivewPartialSnapshotOption> partialPreviewSnapshotOptions = new ListModel<>();
partialPreviewSnapshotOptions.setItems(Arrays.asList(PreivewPartialSnapshotOption.values()));
setPartialPreviewSnapshotOptions(partialPreviewSnapshotOptions);
}
public static SnapshotModel createNewSnapshotModel(ICommandTarget cancelCommandTarget) {
SnapshotModel model = new SnapshotModel();
model.setTitle(ConstantsManager.getInstance().getConstants().createSnapshotTitle());
model.setHelpTag(HelpTag.create_snapshot);
model.setHashName("create_snapshot"); //$NON-NLS-1$
// the cancel command has to be created be before the call to initialize to avoid race condition
model.setCancelCommand(UICommand.createCancelUiCommand("Cancel", cancelCommandTarget)); //$NON-NLS-1$
return model;
}
@Override
public void initialize() {
super.initialize();
startProgress();
initMessages();
}
private void initMessages() {
if (vm.isRunning() && !vm.getHasAgent()) {
setMessage(ConstantsManager.getInstance().getConstants().liveSnapshotWithNoGuestAgentMsg());
}
if (isValidateByVmSnapshots()) {
initVmSnapshots();
}
else {
initVmDisks();
}
}
private void initVmSnapshots() {
AsyncDataProvider.getInstance().getVmSnapshotList(new AsyncQuery<>(snapshots -> {
if (showWarningForByVmSnapshotsValidation(snapshots)) {
UICommand closeCommand = getCancelCommand()
.setTitle(ConstantsManager.getInstance().getConstants().close());
getCommands().add(closeCommand);
stopProgress();
}
else {
initVmDisks();
}
}), vm.getId());
}
private void initVmDisks() {
AsyncDataProvider.getInstance().getVmDiskList(new AsyncQuery<>(disks -> {
updateSnapshotDisks(disks);
VmModelHelper.sendWarningForNonExportableDisks(SnapshotModel.this, disks, VmModelHelper.WarningType.VM_SNAPSHOT);
getCommands().add(getOnSaveCommand());
getCommands().add(getCancelCommand());
stopProgress();
}), vm.getId());
}
private void updateSnapshotDisks(List<Disk> disks) {
List<DiskImage> diskImages =
disks.stream()
.filter(Disk::isAllowSnapshot)
.map(d -> (DiskImage) d)
.sorted(new DiskByDiskAliasComparator())
.collect(Collectors.toList());
getSnapshotDisks().setItems(diskImages);
}
public void updateVmConfiguration(final AsyncCallback<Void> onUpdateAsyncCallback) {
Snapshot snapshot = getEntity();
if (snapshot == null) {
return;
}
AsyncDataProvider.getInstance().getVmConfigurationBySnapshot(new AsyncQuery<>(vm -> {
Snapshot snapshot1 = getEntity();
if (vm != null && snapshot1 != null) {
setVm(vm);
setDisks(vm.getDiskList());
setNics(vm.getInterfaces());
setApps(Arrays.asList(snapshot1.getAppList() != null ?
snapshot1.getAppList().split(",") : new String[]{})); //$NON-NLS-1$
Collections.sort(getDisks(), new DiskByDiskAliasComparator());
Collections.sort(getNics(), new LexoNumericNameableComparator<>());
}
onUpdateAsyncCallback.onSuccess(null);
}), snapshot.getId());
}
public DiskImage getImageByDiskId(Guid diskId) {
for (DiskImage disk : getEntity().getDiskImages()) {
if (disk.getId().equals(diskId)) {
return disk;
}
}
return null;
}
public boolean validate() {
getDescription().validateEntity(new IValidation[] { new NotEmptyValidation(),
new SpecialAsciiI18NOrNoneValidation(),
new LengthValidation(BusinessEntitiesDefinitions.GENERAL_MAX_SIZE) });
return getDescription().getIsValid();
}
private boolean showWarningForByVmSnapshotsValidation(List<Snapshot> snapshots) {
for (Snapshot snapshot : snapshots) {
if (!validateNewSnapshotByStatus(snapshot.getStatus()) || !validateNewSnapshotByType(snapshot.getType())) {
getDescription().setIsAvailable(false);
getMemory().setIsAvailable(false);
return true;
}
}
return false;
}
private boolean validateNewSnapshotByStatus(SnapshotStatus snapshotStatus) {
switch (snapshotStatus) {
case LOCKED:
setMessage(ConstantsManager.getInstance().getConstants().snapshotCannotBeCreatedLockedSnapshotMsg());
return false;
case IN_PREVIEW:
setMessage(ConstantsManager.getInstance().getConstants().snapshotCannotBeCreatedPreviewSnapshotMsg());
return false;
default:
return true;
}
}
private boolean validateNewSnapshotByType(SnapshotType snapshotType) {
switch (snapshotType) {
case STATELESS:
setMessage(ConstantsManager.getInstance().getConstants().snapshotCannotBeCreatedStatelessSnapshotMsg());
return false;
case PREVIEW:
setMessage(ConstantsManager.getInstance().getConstants().snapshotCannotBeCreatedPreviewSnapshotMsg());
return false;
default:
return true;
}
}
private UICommand getOnSaveCommand() {
return UICommand.createDefaultOkUiCommand("OnSave", this); //$NON-NLS-1$
}
@Override
public void executeCommand(UICommand command) {
super.executeCommand(command);
if ("OnSave".equals(command.getName())) { //$NON-NLS-1$
onSave();
}
}
public void onSave() {
if (getProgress() != null || !validate()) {
return;
}
startProgress();
VM vm = getVm();
ArrayList<VdcActionParametersBase> params = new ArrayList<>();
CreateAllSnapshotsFromVmParameters param =
new CreateAllSnapshotsFromVmParameters(vm.getId(),
getDescription().getEntity(),
getMemory().getEntity(),
getSnapshotDisks().getSelectedItems());
param.setQuotaId(vm.getQuotaId());
params.add(param);
Frontend.getInstance().runMultipleAction(VdcActionType.CreateAllSnapshotsFromVm, params,
result -> {
SnapshotModel localModel = (SnapshotModel) result.getState();
localModel.stopProgress();
getCancelCommand().execute();
}, this);
}
public enum PreivewPartialSnapshotOption {
preserveActiveDisks(ConstantsManager.getInstance().getConstants().preserveActiveDisks()),
excludeActiveDisks(ConstantsManager.getInstance().getConstants().excludeActiveDisks()),
openCustomPreviewDialog(ConstantsManager.getInstance().getConstants().openCustomPreviewDialog());
private String description;
private PreivewPartialSnapshotOption(String description) {
this.description = description;
}
@Override
public String toString() {
return description;
}
}
private boolean isVMWithMemoryCompatible(VM vm) {
if (vm == null || vm.getCustomCompatibilityVersion() != null) {
return true;
}
Version recentClusterVersion = vm.getClusterCompatibilityVersion();
// the cluster version in which the memory snapshot was taken
Version originalClusterVersion = vm.getClusterCompatibilityVersionOrigin();
// The originalClusterVersion is null if the snapshot originates in <3.6 .
// Otherwise it is stored in OVF as OvfProperties.CLUSTER_COMPATIBILITY_VERSION and contains cluster
// version at the time of snapshot's creation. Populated by OvfReader.
return originalClusterVersion != null
&& recentClusterVersion.getMajor() == originalClusterVersion.getMajor()
&& recentClusterVersion.getMinor() == originalClusterVersion.getMinor();
}
public void setOldClusterVersionOfSnapshotWithMemory() {
setOldClusterVersionOfSnapshotWithMemory(getVm());
}
public void setOldClusterVersionOfSnapshotWithMemory(VM vm) {
if (!isVMWithMemoryCompatible(vm)) {
// message regarding old cluster snapshot will be shown
Version originalClusterVersion = vm.getClusterCompatibilityVersionOrigin();
originalClusterVersion = (originalClusterVersion == null) ? Version.v3_6 : originalClusterVersion;
oldClusterVersionOfSnapshotWithMemory = originalClusterVersion;
} else {
oldClusterVersionOfSnapshotWithMemory = null;
}
}
public Version getOldClusterVersionOfSnapshotWithMemory() {
return oldClusterVersionOfSnapshotWithMemory;
}
}