package org.ovirt.engine.ui.uicommonweb.models.storage; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.ovirt.engine.core.common.action.ImportVmTemplateParameters; import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.action.VmTemplateImportExportParameters; import org.ovirt.engine.core.common.businessentities.ArchitectureType; import org.ovirt.engine.core.common.businessentities.Entities; import org.ovirt.engine.core.common.businessentities.StorageDomainSharedStatus; import org.ovirt.engine.core.common.businessentities.StorageDomainType; import org.ovirt.engine.core.common.businessentities.StoragePool; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VmTemplate; import org.ovirt.engine.core.common.businessentities.comparators.LexoNumericNameableComparator; import org.ovirt.engine.core.common.businessentities.profiles.CpuProfile; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.queries.GetAllFromExportDomainQueryParameters; import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.StringHelper; import org.ovirt.engine.ui.frontend.AsyncCallback; import org.ovirt.engine.ui.frontend.Frontend; 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.ConfirmationModel; import org.ovirt.engine.ui.uicommonweb.models.EntityModel; import org.ovirt.engine.ui.uicommonweb.models.templates.ImportTemplateModel; import org.ovirt.engine.ui.uicommonweb.models.templates.TemplateImportDiskListModel; import org.ovirt.engine.ui.uicommonweb.models.vms.ImportTemplateData; import org.ovirt.engine.ui.uicommonweb.models.vms.UnitVmModel; import org.ovirt.engine.ui.uicommonweb.validation.I18NNameValidation; 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.NotInCollectionValidation; import org.ovirt.engine.ui.uicommonweb.validation.ValidationResult; import org.ovirt.engine.ui.uicompat.ConstantsManager; import org.ovirt.engine.ui.uicompat.PropertyChangedEventArgs; import org.ovirt.engine.ui.uicompat.UIConstants; import org.ovirt.engine.ui.uicompat.UIMessages; import org.ovirt.engine.ui.uicompat.external.StringUtils; import com.google.inject.Inject; import com.google.inject.Provider; public class TemplateBackupModel extends ManageBackupModel<VmTemplate> { private ArrayList<Map.Entry<VmTemplate, List<DiskImage>>> extendedItems; private StoragePool pool; protected ImportTemplateModel importModel; protected Provider<? extends ImportTemplateModel> importModelProvider; /** used to save the names that were assigned for VMs which are going * to be created using import in case of choosing multiple VM imports */ protected Set<String> assignedVmNames = new HashSet<>(); protected Map<Guid, Object> cloneObjectMap; protected List<ImportTemplateData> objectsToClone; private static UIConstants constants = ConstantsManager.getInstance().getConstants(); private static UIMessages messages = ConstantsManager.getInstance().getMessages(); @Inject public TemplateBackupModel(Provider<ImportTemplateModel> importModelProvider) { this.importModelProvider = importModelProvider; setTitle(constants.templateImportTitle()); setHelpTag(HelpTag.template_import); setHashName("template_import"); //$NON-NLS-1$ } @Override protected void remove() { if (getWindow() != null) { return; } ConfirmationModel model = new ConfirmationModel(); setConfirmWindow(model); model.setTitle(constants.removeBackedUpTemplatesTitle()); model.setHelpTag(HelpTag.remove_backed_up_template); model.setHashName("remove_backed_up_template"); //$NON-NLS-1$ ArrayList<String> items = new ArrayList<>(); for (VmTemplate template : getSelectedItems()) { items.add(template.getName() + getTemplateVersionNameAndNumber(template)); } model.setItems(items); model.setNote(constants.noteTheDeletedItemsMightStillAppearOntheSubTab()); model.getCommands().add(UICommand.createDefaultOkUiCommand("OnRemove", this)); //$NON-NLS-1$ model.getCommands().add(UICommand.createCancelUiCommand(CANCEL_COMMAND, this)); } private void onRemove() { AsyncDataProvider.getInstance().getDataCentersByStorageDomain(new AsyncQuery<>(pools -> { if (pools != null && !pools.isEmpty()) { pool = pools.get(0); checkVmsDependentOnTemplate(pool.getId(), getEntity().getId()); } }), getEntity().getId()); } private void checkVmsDependentOnTemplate(Guid dataCenterId, Guid storageDomainId) { Frontend.getInstance().runQuery(VdcQueryType.GetVmsFromExportDomain, new GetAllFromExportDomainQueryParameters(dataCenterId, storageDomainId), new AsyncQuery<>(createGetVmsFromExportDomainCallback())); } private AsyncCallback<VdcQueryReturnValue> createGetVmsFromExportDomainCallback() { return returnValue -> { if (returnValue == null || returnValue.getReturnValue() == null || !returnValue.getSucceeded()) { return; } List<VM> vmsInExportDomain = returnValue.getReturnValue(); HashMap<String, List<String>> problematicVmNames = getDependentVMsForTemplates(vmsInExportDomain, getSelectedItems()); if (!problematicVmNames.isEmpty()) { showRemoveTemplateWithDependentVMConfirmationWindow(problematicVmNames); } else { removeTemplateBackup(); } }; } private void showRemoveTemplateWithDependentVMConfirmationWindow(HashMap<String, List<String>> problematicVmNames) { ArrayList<String> missingTemplatesFromVms = new ArrayList<>(); for (Map.Entry<String, List<String>> templateName : problematicVmNames.entrySet()) { List<String> vms = problematicVmNames.get(templateName.getKey()); String vmsListString = StringUtils.join(vms, ", "); //$NON-NLS-1$ missingTemplatesFromVms.add(messages.templatesWithDependentVMs(templateName.getKey(), vmsListString)); } setConfirmWindow(null); ConfirmationModel confirmModel = new ConfirmationModel(); setConfirmWindow(confirmModel); confirmModel.setTitle(constants.removeBackedUpTemplatesWithDependentsVMTitle()); confirmModel.setHelpTag(HelpTag.remove_backed_up_template); confirmModel.setHashName("remove_backed_up_template"); //$NON-NLS-1$ confirmModel.setMessage(constants.theFollowingTemplatesHaveDependentVmsBackupOnExportDomainMsg()); confirmModel.setItems(missingTemplatesFromVms); confirmModel.getCommands().add(UICommand.createDefaultOkUiCommand("RemoveVmTemplates", this)); //$NON-NLS-1$ confirmModel.getCommands().add(UICommand.createCancelUiCommand(CANCEL_CONFIRMATION_COMMAND, this)); } private HashMap<String, List<String>> getDependentVMsForTemplates(List<VM> vmsInExportDomain, List<VmTemplate> templates) { // Build a map between the template ID and the template instance Map<Guid, VmTemplate> templateMap = Entities.businessEntitiesById(templates); // Build a map between the template ID and a list of dependent VMs names HashMap<String, List<String>> problematicVmNames = new HashMap<>(); for (VM vm : vmsInExportDomain) { VmTemplate template = templateMap.get(vm.getVmtGuid()); if (template != null) { List<String> vms = problematicVmNames.get(template.getName()); if (vms == null) { vms = new ArrayList<>(); problematicVmNames.put(template.getName(), vms); } vms.add(vm.getName()); } } return problematicVmNames; } private void removeTemplateBackup() { ArrayList<VdcActionParametersBase> prms = new ArrayList<>(); for (VmTemplate template : getSelectedItems()) { prms.add(new VmTemplateImportExportParameters(template.getId(), getEntity().getId(), pool.getId())); } Frontend.getInstance().runMultipleAction(VdcActionType.RemoveVmTemplateFromImportExport, prms); cancel(); } @Override protected ArchitectureType getArchitectureFromItem(VmTemplate template) { return template.getClusterArch(); } protected String getObjectName(ImportTemplateData templateData) { return templateData.getTemplate().getName(); } protected void setObjectName(ImportTemplateData templateData, String name) { templateData.getTemplate().setName(name); } protected boolean validateSuffix(String suffix, EntityModel entityModel) { for (Object object : objectsToClone) { VmTemplate template = ((ImportTemplateData) object).getTemplate(); if (!validateName(template.getName() + suffix, entityModel, getClonedAppendedNameValidators())) { return false; } } return true; } protected boolean validateName(String newVmName, EntityModel<String> entity, IValidation[] validators) { EntityModel<String> temp = new EntityModel<>(); temp.setIsValid(true); temp.setEntity(newVmName); temp.validateEntity(validators); if (!temp.getIsValid()) { entity.setInvalidityReasons(temp.getInvalidityReasons()); entity.setIsValid(false); } return temp.getIsValid(); } protected int getMaxClonedNameLength() { return UnitVmModel.VM_TEMPLATE_AND_INSTANCE_TYPE_NAME_MAX_LIMIT; } protected String getAlreadyAssignedClonedNameMessage() { return messages.alreadyAssignedClonedTemplateName(); } protected String getSuffixCauseToClonedNameCollisionMessage(String existingName) { return messages.suffixCauseToClonedTemplateNameCollision(existingName); } protected void executeImport() { ImportTemplateModel model = (ImportTemplateModel) getWindow(); if (model.getProgress() != null) { return; } if (!model.validate()) { return; } ArrayList<VdcActionParametersBase> prms = new ArrayList<>(); for (Object object : importModel.getItems()) { ImportTemplateData importData = (ImportTemplateData) object; VmTemplate template = importData.getTemplate(); ImportVmTemplateParameters importVmTemplateParameters = new ImportVmTemplateParameters(model.getStoragePool().getId(), getEntity().getId(), Guid.Empty, model.getCluster().getSelectedItem().getId(), template); if (importModel.getClusterQuota().getSelectedItem() != null && importModel.getClusterQuota().getIsAvailable()) { importVmTemplateParameters.setQuotaId(importModel.getClusterQuota().getSelectedItem().getId()); } CpuProfile cpuProfile = importModel.getCpuProfiles().getSelectedItem(); if (cpuProfile != null) { importVmTemplateParameters.setCpuProfileId(cpuProfile.getId()); } Map<Guid, Guid> map = new HashMap<>(); for (DiskImage disk : template.getDiskList()) { map.put(disk.getId(), importModel.getDiskImportData(disk.getId()).getSelectedStorageDomain().getId()); if (importModel.getDiskImportData(disk.getId()).getSelectedQuota() != null) { disk.setQuotaId(importModel.getDiskImportData(disk.getId()).getSelectedQuota().getId()); } } importVmTemplateParameters.setImageToDestinationDomainMap(map); if (importData.isExistsInSystem() || importData.getClone().getEntity()) { if (!cloneObjectMap.containsKey(template.getId())) { continue; } importVmTemplateParameters.setImportAsNewEntity(true); importVmTemplateParameters.getVmTemplate() .setName(((ImportTemplateData) cloneObjectMap.get(template.getId())).getTemplate().getName()); } prms.add(importVmTemplateParameters); } model.startProgress(); Frontend.getInstance().runMultipleAction(VdcActionType.ImportVmTemplate, prms, result -> { TemplateBackupModel templateBackupModel = (TemplateBackupModel) result.getState(); templateBackupModel.getWindow().stopProgress(); templateBackupModel.cancel(); ArrayList<VdcReturnValueBase> retVals = (ArrayList<VdcReturnValueBase>) result.getReturnValue(); if (retVals != null && templateBackupModel.getSelectedItems().size() == retVals.size()) { StringBuilder importedTemplates = new StringBuilder(); int counter = 0; boolean toShowConfirmWindow = false; for (VmTemplate template : templateBackupModel.getSelectedItems()) { if (retVals.get(counter) != null && retVals.get(counter).isValid()) { importedTemplates.append(template.getName()).append(", "); //$NON-NLS-1$ toShowConfirmWindow = true; } counter++; } if (toShowConfirmWindow) { ConfirmationModel confirmModel = new ConfirmationModel(); templateBackupModel.setConfirmWindow(confirmModel); confirmModel.setTitle(constants.importTemplatesTitle()); confirmModel.setHelpTag(HelpTag.import_template); confirmModel.setHashName("import_template"); //$NON-NLS-1$ confirmModel.setMessage(messages.importProcessHasBegunForTemplates(StringHelper.trimEnd(importedTemplates.toString().trim(), ','))); confirmModel.getCommands().add(new UICommand(CANCEL_CONFIRMATION_COMMAND, templateBackupModel) //$NON-NLS-1$ .setTitle(constants.close()) .setIsDefault(true) .setIsCancel(true) ); } } }, this); } @Override protected void entityPropertyChanged(Object sender, PropertyChangedEventArgs e) { super.entityPropertyChanged(sender, e); if (e.propertyName.equals("storage_domain_shared_status")) { //$NON-NLS-1$ getSearchCommand().execute(); } } @Override protected void syncSearch() { if (getEntity() == null || getEntity().getStorageDomainType() != StorageDomainType.ImportExport || getEntity().getStorageDomainSharedStatus() != StorageDomainSharedStatus.Active) { setItems(Collections.<VmTemplate>emptyList()); } else { AsyncDataProvider.getInstance().getDataCentersByStorageDomain(new AsyncQuery<>(list -> { if (list != null && list.size() > 0) { StoragePool dataCenter = list.get(0); Frontend.getInstance().runQuery(VdcQueryType.GetTemplatesFromExportDomain, new GetAllFromExportDomainQueryParameters(dataCenter.getId(), getEntity().getId()), new AsyncQuery<>((AsyncCallback<VdcQueryReturnValue>) returnValue -> { ArrayList<Map.Entry<VmTemplate, List<DiskImage>>> items1 = new ArrayList<>(); HashMap<VmTemplate, List<DiskImage>> dictionary = returnValue.getReturnValue(); ArrayList<VmTemplate> list1 = new ArrayList<>(); for (Map.Entry<VmTemplate, List<DiskImage>> item : dictionary.entrySet()) { items1.add(item); VmTemplate template = item.getKey(); template.setDiskList(new ArrayList<DiskImage>()); template.getDiskList().addAll(item.getValue()); list1.add(template); } Collections.sort(list1, new LexoNumericNameableComparator<>()); setItems(list1); TemplateBackupModel.this.extendedItems = items1; })); } }), getEntity().getId()); } } @Override protected void restore() { if (getWindow() != null) { return; } if (!validateSingleArchitecture()) { return; } ImportTemplateModel model = importModelProvider.get(); model.setEntity(getEntity().getId()); setWindow(model); model.startProgress(); model.setTitle(ConstantsManager.getInstance().getConstants().importTemplatesTitle()); model.getCommands().add(UICommand.createDefaultOkUiCommand("OnRestore", this)); //$NON-NLS-1$ model.getCommands().add(UICommand.createCancelUiCommand(CANCEL_COMMAND, this)); //$NON-NLS-1$); model.init(getSelectedItems(), getEntity().getId()); model.setTargetArchitecture(getArchitectureFromItem(getSelectedItems().get(0))); // Add 'Close' command model.setCloseCommand(new UICommand(CANCEL_COMMAND, this) //$NON-NLS-1$ .setTitle(ConstantsManager.getInstance().getConstants().close()) .setIsDefault(true) .setIsCancel(true) ); ((TemplateImportDiskListModel) ((ImportTemplateModel) getWindow()).getImportDiskListModel()).setExtendedItems(extendedItems); } @Override public void executeCommand(UICommand command) { switch (command.getName()) { case "OnRemove": //$NON-NLS-1$ onRemove(); break; case "OnRestore": //$NON-NLS-1$ onRestore(); break; case "RemoveVmTemplates": //$NON-NLS-1$ removeTemplateBackup(); break; case "onClone": //$NON-NLS-1$ onClone(); break; case "closeClone": //$NON-NLS-1$ closeClone(); break; case "multipleArchsOK": //$NON-NLS-1$ multipleArchsOK(); break; default: super.executeCommand(command); } } private void onClone() { ImportCloneModel cloneModel = (ImportCloneModel) getConfirmWindow(); if (cloneModel.getApplyToAll().getEntity()) { if (!cloneModel.getNoClone().getEntity()) { String suffix = cloneModel.getSuffix().getEntity(); if (!validateSuffix(suffix, cloneModel.getSuffix())) { return; } for (ImportTemplateData object : objectsToClone) { setObjectName(object, suffix, true); cloneObjectMap.put((Guid) object.getEntity().getQueryableId(), object); } } objectsToClone.clear(); } else { ImportTemplateData object = (ImportTemplateData) cloneModel.getEntity(); if (!cloneModel.getNoClone().getEntity()) { String vmName = cloneModel.getName().getEntity(); if (!validateName(vmName, cloneModel.getName(), getClonedNameValidators())) { return; } setObjectName(object, vmName, false); cloneObjectMap.put((Guid) object.getEntity().getQueryableId(), object); } objectsToClone.remove(object); } setConfirmWindow(null); executeImportClone(); } protected IValidation[] getClonedNameValidators() { final int maxClonedNameLength = getMaxClonedNameLength(); return new IValidation[] { new NotEmptyValidation(), new LengthValidation(maxClonedNameLength), new I18NNameValidation(ConstantsManager.getInstance() .getMessages() .nameMustConataionOnlyAlphanumericChars(maxClonedNameLength)), new UniqueClonedNameValidator(assignedVmNames) }; } private void setObjectName(ImportTemplateData templateData, String input, boolean isSuffix) { String nameForTheClonedVm = isSuffix ? getObjectName(templateData) + input : input; setObjectName(templateData, nameForTheClonedVm); assignedVmNames.add(nameForTheClonedVm); } private void closeClone() { setConfirmWindow(null); clearCachedAssignedVmNames(); } private void multipleArchsOK() { setConfirmWindow(null); } public void onRestore() { importModel = (ImportTemplateModel) getWindow(); if (importModel.getProgress() != null) { return; } if (!importModel.validate()) { return; } cloneObjectMap = new HashMap<>(); objectsToClone = new ArrayList<>(); for (Object object : importModel.getItems()) { ImportTemplateData item = (ImportTemplateData) object; if (item.getClone().getEntity()) { objectsToClone.add(item); } } executeImportClone(); } @Override protected String getListName() { return "TemplateBackupModel"; //$NON-NLS-1$ } protected String getImportConflictTitle() { return constants.importTemplateConflictTitle(); } private void executeImportClone() { // TODO: support running numbers (for suffix) if (objectsToClone.size() == 0) { clearCachedAssignedVmNames(); executeImport(); return; } ImportCloneModel entity = new ImportCloneModel(); Object object = objectsToClone.iterator().next(); entity.setEntity(object); entity.setTitle(getImportConflictTitle()); entity.setHelpTag(HelpTag.import_conflict); entity.setHashName("import_conflict"); //$NON-NLS-1$ entity.getCommands().add(UICommand.createDefaultOkUiCommand("onClone", this)); //$NON-NLS-1$ entity.getCommands().add(UICommand.createCancelUiCommand("closeClone", this)); //$NON-NLS-1$ setConfirmWindow(entity); } private void clearCachedAssignedVmNames() { assignedVmNames.clear(); } protected IValidation[] getClonedAppendedNameValidators() { final int maxClonedNameLength = getMaxClonedNameLength(); return new IValidation[] { new NotEmptyValidation(), new LengthValidation(maxClonedNameLength), new I18NNameValidation(ConstantsManager.getInstance() .getMessages() .newNameWithSuffixCannotContainBlankOrSpecialChars(maxClonedNameLength)), new UniqueClonedAppendedNameValidator(assignedVmNames) }; } private class UniqueClonedAppendedNameValidator extends NotInCollectionValidation { public UniqueClonedAppendedNameValidator(Collection<?> collection) { super(collection); } @Override public ValidationResult validate(Object value) { ValidationResult result = super.validate(value); if (!result.getSuccess()) { result.getReasons().add(getSuffixCauseToClonedNameCollisionMessage((String) value)); } return result; } } private class UniqueClonedNameValidator extends NotInCollectionValidation { public UniqueClonedNameValidator(Collection<?> collection) { super(collection); } @Override public ValidationResult validate(Object value) { ValidationResult result = super.validate(value); if (!result.getSuccess()) { result.getReasons().add(getAlreadyAssignedClonedNameMessage()); } return result; } } /** * In case the template is not base, return template's version name and version number in the format: * " (Version: version-name (version-number))" */ private String getTemplateVersionNameAndNumber(VmTemplate template) { if (template.isBaseTemplate()) { return ""; //$NON-NLS-1$ } return messages.templateVersionNameAndNumber(template.getTemplateVersionName() != null ? template.getTemplateVersionName() : "", //$NON-NLS-1$ template.getTemplateVersionNumber()); }; }