package org.ovirt.engine.ui.uicommonweb.models.hosts; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import org.ovirt.engine.core.common.businessentities.pm.FenceAgent; import org.ovirt.engine.ui.uicommonweb.models.ListModel; import org.ovirt.engine.ui.uicommonweb.models.SortedListModel; import org.ovirt.engine.ui.uicompat.ConstantsManager; import org.ovirt.engine.ui.uicompat.EventArgs; import org.ovirt.engine.ui.uicompat.PropertyChangedEventArgs; import org.ovirt.engine.ui.uicompat.UIConstants; public class FenceAgentListModel extends SortedListModel<FenceAgentModel> { /** * Constant messages. */ final UIConstants constants = ConstantsManager.getInstance().getConstants(); /** * The available Power Management Types. */ private List<String> pmTypes; /** * The host this {@code FenceAgentListModel} is associated with. */ private HostModel hostModel; /** * Update the Power Management types available. This will also update ALL fence agent models part of this * {@code FenceAgentListModel} * @param pmTypes A list of power management type strings. */ public void setPmTypes(List<String> pmTypes) { this.pmTypes = pmTypes; if (getItems() != null) { for (FenceAgentModel model: getItems()) { String selectedItem = model.getPmType().getSelectedItem(); if (selectedItem != null && pmTypes.contains(selectedItem)) { model.getPmType().setItems(pmTypes, selectedItem); } else { model.getPmType().setItems(pmTypes); } } } } /** * Set the associated host, this also updates ALL fence agent models part of this * {@code FenceAgentListModel} * @param hostModel The {@code HostModel} to set. */ public void setHost(HostModel hostModel) { this.hostModel = hostModel; if (getItems() != null) { for (FenceAgentModel model: getItems()) { model.setHost(hostModel); } } } /** * Validate the {@code FenceAgentListModel}. This will return true only if all the fence agents part of this * model validate as true. * @return true if validation passes, false otherwise. */ public boolean validate() { if (getItems() != null) { for (FenceAgentModel model: getItems()) { model.validatePmModels(); if (!model.isValid()) { return false; } } } return true; } /** * Getter for available Power Management types. * @return A list of strings representing the power management types. */ public List<String> getPmTypes() { return this.pmTypes; } /** * Getter for the host model * @return The current {@code HostModel} */ public HostModel getHostModel() { return this.hostModel; } /** * Set the list of {@code FenceAgentModel}s. Since the order of the items matters the setItems takes a {@code List} * instead of a collection. setting the items also updates all the {@code FenceAgentModel} concurrentSelectList */ public void setItems(List<FenceAgentModel> items) { updateConcurrentList(items); super.setItems(items); } /** * Updates the available concurrent select list in all the {@code FenceAgentModel}s in this list model. * @see #updateConcurrentList(List) for the algorithm used to update. */ public void updateConcurrentList() { if (getItems() != null) { updateConcurrentList((List<FenceAgentModel>) getItems()); } } /** * Iterate over the passed in {@code List<FenceAgentModel>} and for each model calculate the possible other * models that can be concurrent with it. This is achieved by looping over the list of FenceAgentModels and * calculating the concurrent string. Each string is then added to a list and the list is set for the current * FenceAgentModel we are calculating the list for. When generating the list we skip over the current model. * @param items The list of {@code FenceAgentModel}s */ public void updateConcurrentList(List<FenceAgentModel> items) { for (FenceAgentModel model: items) { updateConcurrentList(model, items); if (model.getConcurrentSelectList().getItems() == null || model.getConcurrentSelectList().getItems().size() < 2) { //There are less than 2 fence agents, can't make them concurrent, so it should not be available. model.getConcurrentSelectList().setIsAvailable(false); } else { model.getConcurrentSelectList().setIsAvailable(true); } } } /** * Update the concurrent list of the passed in {@code FenceAgentModel} based on the passed in list of models. * @param compareModel The model to generate the list for. * @param items The list of {@code FenceAgentModel}s. */ private void updateConcurrentList(FenceAgentModel compareModel, Collection<FenceAgentModel> items) { List<String> values = new ArrayList<>(); values.add(constants.concurrentFenceAgent()); ListModel<String> concurrentList = compareModel.getConcurrentSelectList(); if (concurrentList != null && concurrentList.getItems() != null && compareModel.hasAddress() && items != null) { for (FenceAgentModel model: items) { if (!model.equals(compareModel) && model.hasAddress()) { values.add(createConcurrentOptionString(model)); } } } if (concurrentList != null && concurrentList.getSelectedItem() != null && values.indexOf(concurrentList.getSelectedItem()) > -1) { concurrentList.setItems(values, concurrentList.getSelectedItem()); } else if (concurrentList != null ) { concurrentList.setItems(values); } } /** * Build a string that represents the {@code FenceAgentModel} or a concurrent group of {@code FenceAgentModel}s. * It is either the 'displayString' from the model, OR a comma separated string of displayStrings. The comma * separated string is generated if the model is in a concurrent group. * * @param model The model to generate the concurrent option string for. * @return A string for the concurrent selection list. */ private String createConcurrentOptionString(FenceAgentModel model) { StringBuilder builder = new StringBuilder(); builder.append(model.getDisplayString()); for (FenceAgentModel concurrentModel: model.getConcurrentList()) { builder.append(", " + concurrentModel.getDisplayString()); //$NON-NLS-1$ } return builder.toString(); } /** * Make the passed in {@code FenceAgentModel} concurrent with the model represented by the 'selectedString'. The * model (or concurrent group) is looked up using the string. The concurrentModel is then added to the model/group. * @param concurrentModel The model to add to the model/group represented by the selectedString. * @param selectedString A string representing a model/group. */ public void makeConcurrent(FenceAgentModel concurrentModel, String selectedString) { for (FenceAgentModel model : getItems()) { if (selectedString != null && selectedString.contains(createConcurrentOptionString(model))) { makeConcurrent(concurrentModel, model); return; } } } /** * Make the passed in concurrentModel concurrent with the targetModel. The target model is either a single * {@code FenceAgentModel} or a concurrent group. Once this method is done the concurrentModel will be part * of the targetModels concurrentList. * @param concurrentModel The model to add to the target model concurrent list. * @param targetModel The model the concurrent model will be added to. */ private void makeConcurrent(final FenceAgentModel concurrentModel, final FenceAgentModel targetModel) { if (concurrentModel.getOrder().getEntity() > targetModel.getOrder().getEntity()) { concurrentModel.setOrder(targetModel.getOrder().getEntity()); } else { targetModel.setOrder(concurrentModel.getOrder().getEntity()); } targetModel.getConcurrentList().add(concurrentModel); getItems().remove(concurrentModel); notifyItemListeners(); updateConcurrentList(); targetModel.getConcurrentSelectList().setSelectedItem(createConcurrentOptionString(concurrentModel)); } /** * Remove the passed in {@code FenceAgentModel} from the concurrent group it is in. If the model is not in any * concurrent group this will do nothing. After the model is removed from the concurrent group it is placed back * in the {@code FenceAgentListModel}s item list as a normal fence agent. If the concurrent group the model was * part of is reduced to 1 {@code FenceAgentModel} that will also be added to the item list. * @param value The {@code FenceAgentModel} to remove from the concurrent group. */ public void removeConcurrent(FenceAgentModel value) { for (FenceAgentModel model : getItems()) { if (model.equals(value) && !model.getConcurrentList().isEmpty()) { //The main concurrent model is being removed, move all the remaining models to the first one in the //concurrent list. FenceAgentModel newMainConcurrent = model.getConcurrentList().get(0); newMainConcurrent.getConcurrentList().addAll(model.getConcurrentList()); //Remove itself from the list. newMainConcurrent.getConcurrentList().remove(newMainConcurrent); newMainConcurrent.setOrder(value.getOrder().getEntity()); getItems().add(newMainConcurrent); //Sort to make sure the 'new' is at the end. sortAndFixOrder(); if (newMainConcurrent.getConcurrentList().isEmpty()) { newMainConcurrent.getConcurrentSelectList().setIsAvailable(true); newMainConcurrent.getConcurrentSelectList() .setSelectedItem(newMainConcurrent.getConcurrentListFirstItem()); } model.setOrder(getItems().size()); model.getConcurrentList().clear(); model.getConcurrentSelectList().setSelectedItem(model.getConcurrentListFirstItem()); notifyItemListeners(); return; } else { //Check if any of the concurrent models are the one being removed from the group. for (FenceAgentModel concurrentModel: model.getConcurrentList()) { if (concurrentModel.equals(value)) { model.getConcurrentList().remove(concurrentModel); concurrentModel.getConcurrentSelectList().setSelectedItem( concurrentModel.getConcurrentListFirstItem()); if (model.getConcurrentList().isEmpty()) { model.getConcurrentSelectList().setIsAvailable(true); model.getConcurrentSelectList().setSelectedItem(model.getConcurrentListFirstItem()); } getItems().add(concurrentModel); //Sort to make sure the 'new' is at the end. sortAndFixOrder(); updateConcurrentList(concurrentModel, getItems()); value.setOrder(getItems().size()); notifyItemListeners(); return; } } } } } /** * Notify any item listeners that the items have been changed. */ public void notifyItemListeners() { List<FenceAgentModel> sortedList = sortAndFixOrder(); setItems(sortedList); getItemsChangedEvent().raise(this, EventArgs.EMPTY); onPropertyChanged(new PropertyChangedEventArgs("Items")); //$NON-NLS-1$ } /** * Sort the {@code FenceAgentModel}s based on their order, and fix any gaps in the order count. * @return Sorted {@code FenceAgentModel}s that don't have any gaps in the order attribute. */ public List<FenceAgentModel> sortAndFixOrder() { List<FenceAgentModel> sortedList = (List<FenceAgentModel>) getItems(); Collections.sort(sortedList, FenceAgentModel.orderComparable); //Fix any gaps in the list. for (FenceAgentModel model: sortedList) { model.setOrder(sortedList.indexOf(model) + 1); } return sortedList; } /** * Move the passed in {@code FenceAgentModel} up in the items list. Also change the order value of the passed in * model and the model it changed places with. If the model passed in is the first model don't do anything. * @param movingModel The model to move up. */ public void moveUp(FenceAgentModel movingModel) { List<FenceAgentModel> sortedList = (List<FenceAgentModel>) getItems(); int movingModelIndex = sortedList.indexOf(movingModel); if (movingModelIndex > 0) { sortedList.get(movingModelIndex).setOrder(sortedList.get(movingModelIndex).getOrder().getEntity() - 1); sortedList.get(movingModelIndex - 1) .setOrder(sortedList.get(movingModelIndex - 1).getOrder().getEntity() + 1); notifyItemListeners(); } } /** * Move the passed in {@code FenceAgentModel} down in the items list. Also change the order value of the passed in * model and the model it changed places with. If the model passed in is the last model don't do anything. * @param movingModel The model to move down. */ public void moveDown(FenceAgentModel movingModel) { List<FenceAgentModel> sortedList = (List<FenceAgentModel>) getItems(); int movingModelIndex = sortedList.indexOf(movingModel); if (movingModelIndex < sortedList.size() - 1) { sortedList.get(movingModelIndex).setOrder(sortedList.get(movingModelIndex).getOrder().getEntity() + 1); sortedList.get(movingModelIndex + 1) .setOrder(sortedList.get(movingModelIndex + 1).getOrder().getEntity() - 1); notifyItemListeners(); } } /** * Return a list of {@code FenceAgent}s based on the list of {@code FenceAgentModel}s containing in this list model * @return A list of {@code FenceAgent}s */ public List<FenceAgent> getFenceAgents() { List<FenceAgent> agents = new LinkedList<>(); for (FenceAgentModel agentModel: getItems()) { FenceAgent agent = createFenceAgentFromModel(agentModel); if (!agentModel.getConcurrentList().isEmpty()) { for (FenceAgentModel concurrentAgentModel: agentModel.getConcurrentList()) { FenceAgent concurrentAgent = createFenceAgentFromModel(concurrentAgentModel); agents.add(concurrentAgent); } } agents.add(agent); } return agents; } /** * Create a {@code FenceAgent} from the passed in {@code FenceAgentModel} * @param agentModel The model to create the {@code FenceAgent} out of. * @return A {@code FenceAgent} based on the passed in model. */ private FenceAgent createFenceAgentFromModel(FenceAgentModel agentModel) { FenceAgent agent = new FenceAgent(); agent.setIp(agentModel.getManagementIp().getEntity()); agent.setUser(agentModel.getPmUserName().getEntity()); agent.setPassword(agentModel.getPmPassword().getEntity()); agent.setType(agentModel.getPmType().getSelectedItem()); agent.setOptionsMap(agentModel.getPmOptionsMap()); if (agentModel.getPmEncryptOptions().getEntity() != null) { agent.setEncryptOptions(agentModel.getPmEncryptOptions().getEntity()); } else { agent.setEncryptOptions(false); } if (agentModel.getPmPort() != null && agentModel.getPmPort().getEntity() != null) { agent.setPort(agentModel.getPmPort().getEntity()); } agent.setOrder(agentModel.getOrder().getEntity()); return agent; } /** * Remove the passed in model from the items list and notify listeners of changes. * @param fenceAgentModel The model to remove. */ public void removeItem(FenceAgentModel fenceAgentModel) { getItems().remove(fenceAgentModel); updateConcurrentList(); sortAndFixOrder(); } }