package org.ovirt.engine.ui.uicommonweb.models.vms;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.ovirt.engine.core.common.action.AttachDetachVmDiskParameters;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.businessentities.comparators.DiskByDiskAliasComparator;
import org.ovirt.engine.core.common.businessentities.storage.Disk;
import org.ovirt.engine.core.common.businessentities.storage.DiskInterface;
import org.ovirt.engine.core.common.businessentities.storage.DiskStorageType;
import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement;
import org.ovirt.engine.ui.frontend.AsyncCallback;
import org.ovirt.engine.ui.frontend.Frontend;
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.uicompat.ConstantsManager;
import org.ovirt.engine.ui.uicompat.EventArgs;
import org.ovirt.engine.ui.uicompat.IEventListener;
import org.ovirt.engine.ui.uicompat.IFrontendActionAsyncCallback;
import org.ovirt.engine.ui.uicompat.UIConstants;
public class AttachDiskModel extends NewDiskModel {
protected static final UIConstants constants = ConstantsManager.getInstance().getConstants();
private Map<DiskStorageType, ListModel<EntityModel<DiskModel>>> attachableDisksMap;
private EntityModel<String> messageLabel;
private EntityModel<String> warningLabel;
public AttachDiskModel() {
attachableDisksMap = new HashMap<>();
attachableDisksMap.put(DiskStorageType.IMAGE, new ListModel<EntityModel<DiskModel>>());
attachableDisksMap.put(DiskStorageType.LUN, new ListModel<EntityModel<DiskModel>>());
attachableDisksMap.put(DiskStorageType.CINDER, new ListModel<EntityModel<DiskModel>>());
setWarningLabel(new EntityModel<String>());
getWarningLabel().setIsAvailable(false);
setMessageLabel(new EntityModel<String>());
getMessageLabel().setIsAvailable(false);
addListeners();
}
public Map<DiskStorageType, ListModel<EntityModel<DiskModel>>> getAttachableDisksMap() {
return attachableDisksMap;
}
@Override
public void flush() {
// no need to do any flush
}
@Override
public void initialize() {
super.initialize();
getIsPlugged().setIsAvailable(true);
if (getVm().getId() != null) {
loadAttachableDisks();
}
getIsBootable().setIsChangeable(true);
}
@Override
public void updateCanSetBoot(List<Disk> vmDisks) {
boolean bootDiskFound = false;
for (Disk disk : vmDisks) {
if (disk.getDiskVmElementForVm(getVmId()).isBoot()) {
bootDiskFound = true;
break;
}
}
if (bootDiskFound) {
getIsBootable().setIsChangeable(false);
}
else {
addBootChangeListener();
}
}
@Override
protected void updatePassDiscardAvailability() {
// Pass discard is not relevant for the attach
// disk window, only for the new and edit windows.
}
@Override
protected void updatePassDiscardChangeability() {
// Pass discard is not relevant for the attach
// disk window, only for the new and edit windows.
}
private void addBootChangeListener() {
// Whenever the changeability of isBootable changes propagate it to all child DiskModels to reflect it in the view
getIsBootable().getPropertyChangedEvent().addListener((ev, sender, args) -> {
if ("IsChangable".equals(args.propertyName)) { //$NON-NLS-1$
for (ListModel<EntityModel<DiskModel>> disks : getAttachableDisksMap().values()) {
if (disks.getItems() != null) {
for (EntityModel<DiskModel> disk : disks.getItems()) {
boolean isChangeBootAllowed = ((EntityModel) sender).getIsChangable();
disk.getEntity().getIsBootable().setIsChangeable(isChangeBootAllowed);
}
disks.getItemsChangedEvent().raise(disks, EventArgs.EMPTY);
}
}
}
});
}
public void loadAttachableDisks() {
doLoadAttachableDisks(new GetDisksCallback(DiskStorageType.IMAGE),
new GetDisksCallback(DiskStorageType.LUN),
new GetDisksCallback(DiskStorageType.CINDER));
}
protected void doLoadAttachableDisks(GetDisksCallback imageCallback, GetDisksCallback lunCallback,
GetDisksCallback cinderCallback) {
AsyncDataProvider.getInstance().getAllAttachableDisks(
new AsyncQuery<>(imageCallback), getVm().getStoragePoolId(), getVm().getId());
AsyncDataProvider.getInstance().getAllAttachableDisks(
new AsyncQuery<>(lunCallback), null, getVm().getId());
AsyncDataProvider.getInstance().getAllAttachableDisks(
new AsyncQuery<>(cinderCallback), getVm().getStoragePoolId(), getVm().getId());
}
class GetDisksCallback implements AsyncCallback<List<Disk>> {
private DiskStorageType diskStorageType;
GetDisksCallback(DiskStorageType diskStorageType) {
this.diskStorageType = diskStorageType;
}
@Override
public void onSuccess(List<Disk> returnValue) {
List<Disk> disks = adjustReturnValue(returnValue);
Collections.sort(disks, new DiskByDiskAliasComparator());
final List<DiskModel> diskModels = DiskModel.disksToDiskModelList(disks);
AsyncDataProvider.getInstance().getDiskInterfaceList(getVm().getVmOsId(), getVm().getClusterCompatibilityVersion(), new AsyncQuery<>(
diskInterfaces -> AsyncDataProvider.getInstance().isVirtioScsiEnabledForVm(new AsyncQuery<>(
virtioScsiEnabledReturnValue -> {
boolean virtioScsiEnabled = Boolean.TRUE.equals(virtioScsiEnabledReturnValue);
if (!virtioScsiEnabled) {
diskInterfaces.remove(DiskInterface.VirtIO_SCSI);
}
for (DiskModel diskModel : diskModels) {
diskModel.getDiskInterface().setItems(diskInterfaces);
diskModel.getDiskInterface().setSelectedItem(virtioScsiEnabled ? DiskInterface.VirtIO_SCSI : DiskInterface.VirtIO);
if (getIsBootable().getIsChangable()) { // no point in adding a listener if the value cam't be changed
diskModel.getIsBootable().getEntityChangedEvent().addListener((ev, sender, args) -> {
boolean isBootableMarked = (Boolean) ((EntityModel) sender).getEntity();
getIsBootable().setIsChangeable(!isBootableMarked);
});
}
else {
diskModel.getIsBootable().setChangeProhibitionReason(constants.onlyOneBootableDisk());
diskModel.getIsBootable().setIsChangeable(false);
}
}
List<EntityModel<DiskModel>> entities =
diskModels.stream()
.filter(m -> m.getDisk().getDiskStorageType() == diskStorageType)
.map(EntityModel::new)
.collect(Collectors.toList());
initAttachableDisks(entities);
}), getVmId())));
}
protected void initAttachableDisks(List<EntityModel<DiskModel>> entities) {
getAttachableDisksMap().get(diskStorageType).setItems(entities);
}
protected List<Disk> adjustReturnValue(List<Disk> returnValue) {
return returnValue;
}
}
@Override
public boolean validate() {
if (isNoSelection()) {
getInvalidityReasons().add(constants.noDisksSelected());
setIsValid(false);
return false;
}
return true;
}
@Override
public void store(IFrontendActionAsyncCallback callback) {
if (getProgress() != null || !validate()) {
return;
}
ArrayList<VdcActionType> actionTypes = new ArrayList<>();
ArrayList<VdcActionParametersBase> paramerterList = new ArrayList<>();
ArrayList<IFrontendActionAsyncCallback> callbacks = new ArrayList<>();
IFrontendActionAsyncCallback onFinishCallback = callback != null ? callback : result -> {
NewDiskModel diskModel = (NewDiskModel) result.getState();
diskModel.stopProgress();
diskModel.cancel();
};
List<EntityModel<DiskModel>> disksToAttach = getSelectedDisks();
for (int i = 0; i < disksToAttach.size(); i++) {
DiskModel disk = disksToAttach.get(i).getEntity();
/*
IDE disks can be activated only when the VM is down.
Other disks can be hot plugged.
*/
boolean activate = false;
if (getIsPlugged().getEntity()) {
activate = disk.getDiskInterface().getSelectedItem() == DiskInterface.IDE ?
getVm().getStatus() == VMStatus.Down : true;
}
DiskVmElement dve = new DiskVmElement(disk.getDisk().getId(), getVm().getId());
dve.setBoot(disk.getIsBootable().getEntity());
dve.setDiskInterface(disk.getDiskInterface().getSelectedItem());
// Disk is attached to VM as read only or not, null is applicable only for floating disks
// but this is not a case here.
AttachDetachVmDiskParameters parameters = new AttachDetachVmDiskParameters(dve , activate,
Boolean.TRUE.equals(disk.isReadOnly()));
actionTypes.add(VdcActionType.AttachDiskToVm);
paramerterList.add(parameters);
callbacks.add(i == disksToAttach.size() - 1 ? onFinishCallback : null);
}
startProgress();
Frontend.getInstance().runMultipleActions(actionTypes, paramerterList, callbacks, null, this);
}
public EntityModel<String> getWarningLabel() {
return warningLabel;
}
public void setWarningLabel(EntityModel<String> value) {
warningLabel = value;
}
public EntityModel<String> getMessageLabel() {
return messageLabel;
}
public void setMessageLabel(EntityModel<String> messageLabel) {
this.messageLabel = messageLabel;
}
private boolean isNoSelection() {
for (ListModel<EntityModel<DiskModel>> listModel : attachableDisksMap.values()) {
boolean multipleSelectionSelected = listModel.getSelectedItems() != null && !listModel.getSelectedItems().isEmpty();
boolean singleSelectionSelected = listModel.getSelectedItem() != null;
if (multipleSelectionSelected || singleSelectionSelected) {
return false;
}
}
return true;
}
public List<EntityModel<DiskModel>> getSelectedDisks() {
List<EntityModel<DiskModel>> selectedDisks = new ArrayList<>();
for (ListModel<EntityModel<DiskModel>> listModel : attachableDisksMap.values()) {
if (listModel.getSelectedItems() != null && !listModel.getSelectedItems().isEmpty()) {
selectedDisks.addAll(listModel.getSelectedItems());
}
if (listModel.getSelectedItem() != null) {
selectedDisks.add(listModel.getSelectedItem());
}
}
return selectedDisks;
}
private boolean isSelectedDiskInterfaceIDE(List<EntityModel<DiskModel>> selectedDisks) {
for (EntityModel<DiskModel> selectedDisk : selectedDisks) {
if (selectedDisk.getEntity().getDiskInterface().getSelectedItem() == DiskInterface.IDE) {
return true;
}
}
return false;
}
private void addListeners() {
addSelectedItemsChangedListener();
addIsPluggedEntityChangedListener();
}
private void updateWarningLabel() {
getWarningLabel().setIsAvailable(false);
if (getIsPlugged().getEntity().equals(Boolean.TRUE) && getVm().getStatus() != VMStatus.Down) {
List<EntityModel<DiskModel>> selectedDisks = getSelectedDisks();
if (selectedDisks != null && isSelectedDiskInterfaceIDE(selectedDisks)) {
getWarningLabel().setEntity(constants.ideDisksWillBeAttachedButNotActivated());
getWarningLabel().setIsAvailable(true);
}
}
}
private void addSelectedItemsChangedListener() {
IEventListener<EventArgs> selectionChangedListener = (ev, sender, args) -> updateWarningLabel();
attachableDisksMap.get(DiskStorageType.IMAGE).
getSelectedItemsChangedEvent().addListener(selectionChangedListener);
attachableDisksMap.get(DiskStorageType.CINDER).
getSelectedItemsChangedEvent().addListener(selectionChangedListener);
attachableDisksMap.get(DiskStorageType.LUN).
getSelectedItemsChangedEvent().addListener(selectionChangedListener);
}
private void addIsPluggedEntityChangedListener() {
IEventListener<EventArgs> entityChangedListener = (ev, sender, args) -> updateWarningLabel();
getIsPlugged().getEntityChangedEvent().addListener(entityChangedListener);
}
}