package org.ovirt.engine.ui.uicommonweb.models.gluster; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.comparators.LexoNumericComparator; import org.ovirt.engine.core.common.businessentities.gluster.GlusterBrickEntity; import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeType; import org.ovirt.engine.core.common.businessentities.gluster.StorageDevice; import org.ovirt.engine.ui.uicommonweb.UICommand; import org.ovirt.engine.ui.uicommonweb.dataprovider.AsyncDataProvider; import org.ovirt.engine.ui.uicommonweb.models.EntityModel; import org.ovirt.engine.ui.uicommonweb.models.ListModel; import org.ovirt.engine.ui.uicommonweb.models.Model; import org.ovirt.engine.ui.uicommonweb.models.volumes.VolumeListModel; import org.ovirt.engine.ui.uicommonweb.validation.IValidation; import org.ovirt.engine.ui.uicommonweb.validation.IntegerValidation; import org.ovirt.engine.ui.uicommonweb.validation.NotEmptyValidation; import org.ovirt.engine.ui.uicompat.ConstantsManager; public class VolumeBrickModel extends Model { EntityModel<GlusterVolumeType> volumeType; EntityModel<Integer> replicaCount; EntityModel<Integer> stripeCount; ListModel<VDS> servers; EntityModel<String> brickDirectory; ListModel<String> bricksFromServer; EntityModel<Boolean> showBricksList; ListModel<EntityModel<GlusterBrickEntity>> bricks; EntityModel<Boolean> force; private UICommand addBrickCommand; private UICommand removeBricksCommand; private UICommand removeAllBricksCommand; private UICommand moveBricksUpCommand; private UICommand moveBricksDownCommand; public VolumeBrickModel() { setVolumeType(new EntityModel<GlusterVolumeType>()); setReplicaCount(new EntityModel<Integer>()); getReplicaCount().setEntity(VolumeListModel.REPLICATE_COUNT_DEFAULT); getReplicaCount().setIsChangeable(false); setStripeCount(new EntityModel<Integer>()); getStripeCount().setEntity(VolumeListModel.STRIPE_COUNT_DEFAULT); getStripeCount().setIsChangeable(false); setServers(new ListModel<VDS>()); setBrickDirectory(new EntityModel<String>()); setBricksFromServer(new ListModel<String>()); setShowBricksList(new EntityModel<Boolean>()); setBricks(new ListModel<EntityModel<GlusterBrickEntity>>()); setForce(new EntityModel<Boolean>()); getForce().setEntity(false); setAddBrickCommand(new UICommand("AddBrick", this)); //$NON-NLS-1$ setRemoveBricksCommand(new UICommand("RemoveBricks", this)); //$NON-NLS-1$ setRemoveAllBricksCommand(new UICommand("RemoveAllBricks", this)); //$NON-NLS-1$ getAddBrickCommand().setTitle(ConstantsManager.getInstance().getConstants().addBricksButtonLabel()); getRemoveBricksCommand().setTitle(ConstantsManager.getInstance().getConstants().removeBricksButtonLabel()); getRemoveAllBricksCommand().setTitle(ConstantsManager.getInstance().getConstants().removeAllBricksButtonLabel()); getRemoveBricksCommand().setIsExecutionAllowed(false); getRemoveAllBricksCommand().setIsExecutionAllowed(false); setMoveBricksUpCommand(new UICommand("MoveBricksUp", this)); //$NON-NLS-1$ setMoveBricksDownCommand(new UICommand("MoveBricksDown", this)); //$NON-NLS-1$ getMoveBricksUpCommand().setTitle(ConstantsManager.getInstance().getConstants().moveBricksUpButtonLabel()); getMoveBricksDownCommand().setTitle(ConstantsManager.getInstance().getConstants().moveBricksDownButtonLabel()); getMoveBricksUpCommand().setIsExecutionAllowed(false); getMoveBricksDownCommand().setIsExecutionAllowed(false); getShowBricksList().getEntityChangedEvent().addListener((ev, sender, args) -> { if (getShowBricksList().getEntity()) { // Show the brick list and hide the text box for entering brick dir getBricksFromServer().setIsAvailable(true); getBrickDirectory().setIsAvailable(false); updateBricksFromHost(); } else { // Hide the brick list and show the text box for entering brick dir getBricksFromServer().setIsAvailable(false); getBrickDirectory().setIsAvailable(true); } }); getBricks().getSelectedItemsChangedEvent().addListener((ev, sender, args) -> updateSelectedBricksActions()); getBricks().getItemsChangedEvent().addListener((ev, sender, args) -> { if (bricks.getItems() != null && bricks.getItems().iterator().hasNext()) { getRemoveAllBricksCommand().setIsExecutionAllowed(true); } else { getRemoveAllBricksCommand().setIsExecutionAllowed(false); } }); getServers().getSelectedItemChangedEvent().addListener((ev, sender, args) -> { if (getShowBricksList().getEntity()) { updateBricksFromHost(); } }); } private void updateBricksFromHost() { VDS selectedServer = getServers().getSelectedItem(); if (selectedServer != null) { AsyncDataProvider.getInstance().getUnusedBricksFromServer(new AsyncQuery<>(bricks -> { List<String> brickDirectories = new ArrayList<>(); for (StorageDevice brick : bricks) { String mountPoint = brick.getMountPoint(); if (mountPoint != null && !mountPoint.isEmpty()) { // Gluster requires a directory under the mount point, not the mount point itself as a brick // directory. So adding a directory with name of the brick under the mount point. brickDirectories.add(mountPoint + mountPoint.substring(mountPoint.lastIndexOf("/"))); //$NON-NLS-1$ } } getBricksFromServer().setItems(brickDirectories); }), selectedServer.getId()); } } public void setIsBrickProvisioningSupported() { getShowBricksList().setIsAvailable(true); getShowBricksList().setEntity(true); } private void updateSelectedBricksActions() { if (bricks.getSelectedItems() == null || bricks.getSelectedItems().size() == 0) { getRemoveBricksCommand().setIsExecutionAllowed(false); } else { getRemoveBricksCommand().setIsExecutionAllowed(true); } if (bricks.getItems() == null || bricks.getSelectedItems() == null || bricks.getSelectedItems().size() != 1) { getMoveBricksUpCommand().setIsExecutionAllowed(false); getMoveBricksDownCommand().setIsExecutionAllowed(false); } else { EntityModel<GlusterBrickEntity> selectedItem = bricks.getSelectedItems().get(0); List<EntityModel<GlusterBrickEntity>> items = (List<EntityModel<GlusterBrickEntity>>) bricks.getItems(); int position = items.indexOf(selectedItem); if (position == 0) { getMoveBricksUpCommand().setIsExecutionAllowed(false); } else { getMoveBricksUpCommand().setIsExecutionAllowed(true); } if (position == (items.size() - 1)) { getMoveBricksDownCommand().setIsExecutionAllowed(false); } else { getMoveBricksDownCommand().setIsExecutionAllowed(true); } } } public UICommand getAddBrickCommand() { return addBrickCommand; } private void setAddBrickCommand(UICommand value) { addBrickCommand = value; } public UICommand getRemoveBricksCommand() { return removeBricksCommand; } private void setRemoveBricksCommand(UICommand value) { removeBricksCommand = value; } public UICommand getRemoveAllBricksCommand() { return removeAllBricksCommand; } private void setRemoveAllBricksCommand(UICommand value) { removeAllBricksCommand = value; } public UICommand getMoveBricksUpCommand() { return moveBricksUpCommand; } private void setMoveBricksUpCommand(UICommand value) { moveBricksUpCommand = value; } public UICommand getMoveBricksDownCommand() { return moveBricksDownCommand; } private void setMoveBricksDownCommand(UICommand value) { moveBricksDownCommand = value; } public EntityModel<GlusterVolumeType> getVolumeType() { return volumeType; } public void setVolumeType(EntityModel<GlusterVolumeType> volumeType) { this.volumeType = volumeType; } public EntityModel<Integer> getReplicaCount() { return replicaCount; } public Integer getReplicaCountValue() { return replicaCount.getEntity(); } public void setReplicaCount(EntityModel<Integer> replicaCount) { this.replicaCount = replicaCount; } public EntityModel<Integer> getStripeCount() { return stripeCount; } public Integer getStripeCountValue() { return stripeCount.getEntity(); } public void setStripeCount(EntityModel<Integer> stripeCount) { this.stripeCount = stripeCount; } public ListModel<VDS> getServers() { return servers; } public void setServers(ListModel<VDS> servers) { this.servers = servers; } public EntityModel<String> getBrickDirectory() { return brickDirectory; } public void setBrickDirectory(EntityModel<String> brickDirectory) { this.brickDirectory = brickDirectory; } public ListModel<EntityModel<GlusterBrickEntity>> getBricks() { return bricks; } public void setBricks(ListModel<EntityModel<GlusterBrickEntity>> selectedBricks) { this.bricks = selectedBricks; } public EntityModel<Boolean> getForce() { return force; } public void setForce(EntityModel<Boolean> force) { this.force = force; } public boolean validateAddBricks(GlusterVolumeType selectedVolumeType) { boolean valid = true; valid = getBricks().getItems() != null && getBricks().getItems().iterator().hasNext(); return valid; } private void addBrick() { VDS server = servers.getSelectedItem(); String brickDir = null; if (getShowBricksList().getEntity()) { brickDir = bricksFromServer.getSelectedItem(); } else { brickDir = getBrickDirectory().getEntity(); } if (server == null) { setMessage(ConstantsManager.getInstance().getConstants().emptyServerBrickMsg()); return; } if (brickDir == null || brickDir.trim().length() == 0) { setMessage(ConstantsManager.getInstance().getConstants().emptyBrickDirectoryMsg()); return; } brickDir = brickDir.trim(); if (!validateBrickDirectory(brickDir)) { return; } GlusterBrickEntity brickEntity = new GlusterBrickEntity(); brickEntity.setServerId(server.getId()); brickEntity.setServerName(server.getHostName()); brickEntity.setBrickDirectory(brickDir); EntityModel<GlusterBrickEntity> entityModel = new EntityModel<>(brickEntity); List<EntityModel<GlusterBrickEntity>> items = (List<EntityModel<GlusterBrickEntity>>) bricks.getItems(); if (items == null) { items = new ArrayList<>(); } for (EntityModel<GlusterBrickEntity> model : items) { GlusterBrickEntity existingBrick = model.getEntity(); if (existingBrick.getServerId().equals(brickEntity.getServerId()) && existingBrick.getBrickDirectory().equals(brickEntity.getBrickDirectory())) { setMessage(ConstantsManager.getInstance().getConstants().duplicateBrickMsg()); return; } } items.add(entityModel); bricks.setItems(null); bricks.setItems(items); clearBrickDetails(); } private boolean validateBrickDirectory(String brickDir) { if (brickDir.length() < 2) { setMessage(ConstantsManager.getInstance().getConstants().invalidBrickDirectoryAtleastTwoCharacterseMsg()); return false; } if(!brickDir.startsWith("/")) { //$NON-NLS-1$ setMessage(ConstantsManager.getInstance().getConstants().invalidBrickDirectoryStartWithSlashMsg()); return false; } if (brickDir.contains(" ")) { //$NON-NLS-1$ setMessage(ConstantsManager.getInstance().getConstants().invalidBrickDirectoryContainsSpaceMsg()); return false; } if (brickDir.charAt(1) == '/' || (brickDir.charAt(1) == '.' && brickDir.length() == 2) || brickDir.charAt(1) == '*' || (brickDir.length() == 3 && brickDir.charAt(1) == '.' && brickDir.charAt(2) == '.')) { setMessage(ConstantsManager.getInstance().getConstants().invalidBrickDirectoryMsg()); return false; } return true; } private void clearBrickDetails() { getBrickDirectory().setEntity(null); setMessage(null); } private void removeBricks() { List<EntityModel<GlusterBrickEntity>> items = (List<EntityModel<GlusterBrickEntity>>) bricks.getItems(); List<EntityModel<GlusterBrickEntity>> selectedItems = bricks.getSelectedItems(); if (items == null || selectedItems == null) { return; } items.removeAll(selectedItems); bricks.setItems(null); bricks.setItems(items); } private void removeAllBricks() { List<EntityModel<GlusterBrickEntity>> items = (List<EntityModel<GlusterBrickEntity>>) bricks.getItems(); if (items == null) { return; } items.clear(); bricks.setItems(null); bricks.setItems(items); } @SuppressWarnings("unchecked") private void moveItemsUpDown(boolean isUp) { List<EntityModel<GlusterBrickEntity>> selectedItems = bricks.getSelectedItems(); ArrayList<EntityModel<GlusterBrickEntity>> items = new ArrayList<>(bricks.getItems()); for (EntityModel<GlusterBrickEntity> selectedItem : selectedItems) { int position = items.indexOf(selectedItem); if (position == -1) { continue; } if (isUp) { if (position > 0) { items.remove(position); items.add(position - 1, selectedItem); } } else { if (position < items.size() - 1) { items.remove(position); items.add(position + 1, selectedItem); } } } bricks.setItems(items); bricks.setSelectedItems(selectedItems); } public boolean validateBrickCount(GlusterVolumeType selectedVolumeType, boolean isCreateVolume) { int brickCount = 0; if (bricks.getItems() != null) { brickCount = getBricks().getItems().size(); } int replicaCount = getReplicaCountValue(); int stripeCount = getStripeCountValue(); return validateBrickCount(selectedVolumeType, brickCount, replicaCount, stripeCount, isCreateVolume); } public static boolean validateBrickCount(GlusterVolumeType selectedVolumeType, ListModel bricks, int replicaCount, int stripeCount, boolean isCreateVolume) { int brickCount = 0; if (bricks.getItems() != null) { brickCount = ((List<?>) bricks.getItems()).size(); } return validateBrickCount(selectedVolumeType, brickCount, replicaCount, stripeCount, isCreateVolume); } public static boolean validateBrickCount(GlusterVolumeType selectedVolumeType, int brickCount, int replicaCount, int stripeCount, boolean isCreateVolume) { if (brickCount < 1) { return false; } boolean valid = true; // At the time extending a volume, stripe volume can be converted to a distributed stripe volume // and a replicate volume can be converted to a distributed replicate volume // so the validation will be performed for the corresponding distributed types if (!isCreateVolume) { if (selectedVolumeType == GlusterVolumeType.REPLICATE) { selectedVolumeType = GlusterVolumeType.DISTRIBUTED_REPLICATE; } else if (selectedVolumeType == GlusterVolumeType.STRIPE) { selectedVolumeType = GlusterVolumeType.DISTRIBUTED_STRIPE; } } switch (selectedVolumeType) { case REPLICATE: if (brickCount != replicaCount) { valid = false; } break; case STRIPE: if (brickCount != stripeCount) { valid = false; } break; case DISTRIBUTED_REPLICATE: if ((brickCount % replicaCount) != 0) { valid = false; } break; case DISTRIBUTED_STRIPE: if ((brickCount % stripeCount) != 0) { valid = false; } break; case STRIPED_REPLICATE: if (brickCount != stripeCount * replicaCount) { valid = false; } break; case DISTRIBUTED_STRIPED_REPLICATE: if (brickCount <= stripeCount * replicaCount || (brickCount % (stripeCount * replicaCount)) != 0) { valid = false; } break; } return valid; } public static String getValidationFailedMsg(GlusterVolumeType selectedVolumeType, boolean isCreateVolume) { String validationMsg = null; if (!isCreateVolume) { if (selectedVolumeType == GlusterVolumeType.REPLICATE) { selectedVolumeType = GlusterVolumeType.DISTRIBUTED_REPLICATE; } else if (selectedVolumeType == GlusterVolumeType.STRIPE) { selectedVolumeType = GlusterVolumeType.DISTRIBUTED_STRIPE; } } switch (selectedVolumeType) { case DISTRIBUTE: validationMsg = ConstantsManager.getInstance().getConstants().distriputedVolumeAddBricksMsg(); break; case REPLICATE: validationMsg = ConstantsManager.getInstance().getConstants().replicateVolumeAddBricksMsg(); break; case DISTRIBUTED_REPLICATE: validationMsg = ConstantsManager.getInstance().getConstants().distriputedReplicateVolumeAddBricksMsg(); break; case STRIPE: validationMsg = ConstantsManager.getInstance().getConstants().stripeVolumeAddBricksMsg(); break; case DISTRIBUTED_STRIPE: validationMsg = ConstantsManager.getInstance().getConstants().distriputedStripeVolumeAddBricksMsg(); break; case STRIPED_REPLICATE: validationMsg = ConstantsManager.getInstance().getConstants().stripedReplicateVolumeAddBricksMsg(); break; case DISTRIBUTED_STRIPED_REPLICATE: validationMsg = ConstantsManager.getInstance().getConstants().distriputedStripedReplicateVolumeAddBricksMsg(); break; } return validationMsg; } public boolean validate() { getReplicaCount().setIsValid(true); getStripeCount().setIsValid(true); if (getReplicaCount().getIsAvailable()) { IntegerValidation replicaCountValidation = new IntegerValidation(); replicaCountValidation.setMinimum(2); replicaCountValidation.setMaximum(16); getReplicaCount().validateEntity(new IValidation[] { new NotEmptyValidation(), replicaCountValidation }); } if (getStripeCount().getIsAvailable()) { IntegerValidation stripeCountValidation = new IntegerValidation(); stripeCountValidation.setMinimum(4); stripeCountValidation.setMaximum(16); getStripeCount().validateEntity(new IValidation[] { new NotEmptyValidation(), stripeCountValidation }); } return getReplicaCount().getIsValid() && getStripeCount().getIsValid(); } public boolean validateReplicateBricks() { return validateReplicateBricks(getReplicaCountValue(), null); } public boolean validateReplicateBricks(int oldReplicaCount, List<GlusterBrickEntity> existingBricks) { int replicaCount = getReplicaCountValue(); Set<String> servers = new HashSet<>(); if(replicaCount > oldReplicaCount) { int count = 0; for (GlusterBrickEntity brick : existingBricks) { servers.add(brick.getServerName()); count++; } for (Object model : bricks.getItems()) { if (count > replicaCount) { break; } GlusterBrickEntity brick = (GlusterBrickEntity) ((EntityModel) model).getEntity(); if (servers.contains(brick.getServerName())) { return false; } else { servers.add(brick.getServerName()); } count++; } } else { int count = 0; for (Object model : bricks.getItems()) { count++; GlusterBrickEntity brick = (GlusterBrickEntity) ((EntityModel) model).getEntity(); if (servers.contains(brick.getServerName())) { return false; } else { servers.add(brick.getServerName()); } if (count % replicaCount == 0) { servers.clear(); } } } return true; } public void setHostList(List<VDS> hosts) { Collections.sort(hosts, Comparator.comparing(VDS::getHostName, new LexoNumericComparator())); getServers().setItems(hosts); } @Override public void executeCommand(UICommand command) { super.executeCommand(command); if (command == getAddBrickCommand()) { addBrick(); } else if (command == getRemoveBricksCommand()) { removeBricks(); } else if (command == getRemoveAllBricksCommand()) { removeAllBricks(); } else if (command == getMoveBricksUpCommand()) { moveItemsUpDown(true); } else if (command == getMoveBricksDownCommand()) { moveItemsUpDown(false); } } public ListModel<String> getBricksFromServer() { return bricksFromServer; } public void setBricksFromServer(ListModel<String> bricksFromServer) { this.bricksFromServer = bricksFromServer; } public EntityModel<Boolean> getShowBricksList() { return showBricksList; } public void setShowBricksList(EntityModel<Boolean> showBricksList) { this.showBricksList = showBricksList; } }