Tomas Jelinek has uploaded a new change for review. Change subject: frontend: created instance image widget ......................................................................
frontend: created instance image widget Change-Id: I4bcaa1e40841aa12e64708ec82afdb9daec9fb7f Bug-Url: https://bugzilla.redhat.com/1117489 Signed-off-by: Tomas Jelinek <tjeli...@redhat.com> --- M frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java M frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/AddRemoveRowWidget.java A frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImageLineEditor.java A frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImageLineEditor.ui.xml A frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImagesEditor.java A frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImagesEditor.ui.xml M frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/configure/instancetypes/InstanceTypeListModel.java A frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/InstanceImageLineModel.java A frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/InstanceImagesModel.java M frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java M frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java 11 files changed, 955 insertions(+), 16 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/63/36063/1 diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java index 68da609..846a607 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationConstants.java @@ -562,6 +562,18 @@ @DefaultStringValue("Instance Type") String instanceType(); + @DefaultStringValue("Instance Images") + String instanceImages(); + + @DefaultStringValue("Edit") + String editInstanceImages(); + + @DefaultStringValue("Create") + String addInstanceImages(); + + @DefaultStringValue("Attach") + String attachInstanceImages(); + @DefaultStringValue("Memory Size") String memSizeVmPopup(); diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/AddRemoveRowWidget.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/AddRemoveRowWidget.java index aa675b5..418b9b7 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/AddRemoveRowWidget.java +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/AddRemoveRowWidget.java @@ -231,29 +231,45 @@ @Override public void onClick(ClickEvent event) { - ListIterator<Pair<T, V>> last = items.listIterator(items.size()); - if (!last.hasPrevious()) { // just a precaution; if there's no item, there should be no button + if (vetoRemoveWidget(item, value, widget)) { return; } - if (item == last.previous() && last.hasPrevious()) { // add plus button to previous item - Pair<T, V> previousItem = last.previous(); - getEntry(previousItem.getSecond()).appendButton(createButton(previousItem, true)); - } - - removeEntry(item); - onRemove(value, widget); - - if (items.isEmpty()) { - Pair<T, V> item = addGhostEntry(); - onAdd(item.getFirst(), item.getSecond()); - } + doRemoveItem(item, value, widget); } }); return button; } + protected void doRemoveItem(Pair<T, V> item, T value, V widget) { + ListIterator<Pair<T, V>> last = items.listIterator(items.size()); + if (!last.hasPrevious()) { // just a precaution; if there's no item, there should be no button + return; + } + + if (item == last.previous() && last.hasPrevious()) { // add plus button to previous item + Pair<T, V> previousItem = last.previous(); + getEntry(previousItem.getSecond()).appendButton(createButton(previousItem, true)); + } + + removeEntry(item); + onRemove(value, widget); + + if (items.isEmpty()) { + Pair<T, V> ghostItem = addGhostEntry(); + onAdd(ghostItem.getFirst(), ghostItem.getSecond()); + } + } + + /** + * Lets to decide if we really want to remove this item or not. + * Everything can be removed by default. + */ + protected boolean vetoRemoveWidget(Pair<T, V> item, T value, V widget) { + return false; + } + @SuppressWarnings("unchecked") private AddRemoveRowPanel getEntry(V widget) { return (AddRemoveRowPanel) widget.getParent(); diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImageLineEditor.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImageLineEditor.java new file mode 100644 index 0000000..915a6bc --- /dev/null +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImageLineEditor.java @@ -0,0 +1,138 @@ +package org.ovirt.engine.ui.common.widget.uicommon.instanceimages; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.editor.client.SimpleBeanEditorDriver; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.logical.shared.HasValueChangeHandlers; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HasEnabled; +import com.google.gwt.user.client.ui.Label; +import org.ovirt.engine.ui.common.CommonApplicationConstants; +import org.ovirt.engine.ui.common.idhandler.ElementIdHandler; +import org.ovirt.engine.ui.common.idhandler.HasElementId; +import org.ovirt.engine.ui.common.utils.ElementIdUtils; +import org.ovirt.engine.ui.common.widget.UiCommandButton; +import org.ovirt.engine.ui.common.widget.uicommon.popup.AbstractModelBoundPopupWidget; +import org.ovirt.engine.ui.uicommonweb.models.vms.InstanceImageLineModel; +import org.ovirt.engine.ui.uicompat.Event; +import org.ovirt.engine.ui.uicompat.EventArgs; +import org.ovirt.engine.ui.uicompat.IEventListener; + +public class InstanceImageLineEditor extends AbstractModelBoundPopupWidget<InstanceImageLineModel> implements HasValueChangeHandlers<InstanceImageLineModel>, HasEnabled, HasElementId { + + @UiField + @Path("name.entity") + Label nameLabel; + + @UiField + @Ignore + UiCommandButton createEditButton; + + @UiField + @Ignore + UiCommandButton attachButton; + + private String elementId = DOM.createUniqueId(); + + public interface Driver extends SimpleBeanEditorDriver<InstanceImageLineModel, InstanceImageLineEditor> { + } + + private final Driver driver = GWT.create(Driver.class); + + @UiField + CommonApplicationConstants constants; + + interface WidgetUiBinder extends UiBinder<FlowPanel, InstanceImageLineEditor> { + WidgetUiBinder uiBinder = GWT.create(WidgetUiBinder.class); + } + + interface WidgetIdHandler extends ElementIdHandler<InstanceImageLineEditor> { + WidgetIdHandler idHandler = GWT.create(WidgetIdHandler.class); + } + + public InstanceImageLineEditor() { + initWidget(WidgetUiBinder.uiBinder.createAndBindUi(this)); + WidgetIdHandler.idHandler.generateAndSetIds(this); + + driver.initialize(this); + } + + @Override + public void edit(final InstanceImageLineModel model) { + driver.edit(model); + setupElementIds(model); + + createEditButton.setCommand(model.getCreateEditCommand()); + attachButton.setCommand(model.getAttachCommand()); + + // memory leak maybe? + createEditButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + createEditButton.getCommand().execute(); + } + }); + + attachButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + attachButton.getCommand().execute(); + } + }); + + updateButtonText(model); + + model.getDiskModel().getEntityChangedEvent().addListener(new IEventListener<EventArgs>() { + @Override + public void eventRaised(Event<? extends EventArgs> ev, Object sender, EventArgs args) { + ValueChangeEvent.fire(InstanceImageLineEditor.this, model); + updateButtonText(model); + } + }); + } + + public void setupElementIds(InstanceImageLineModel model) { + String diskAlias = model.getDisk() != null ? model.getDisk().getDiskAlias() : ""; //$NON-NLS-1$ + String composedId = ElementIdUtils.createElementId(elementId, diskAlias); + + nameLabel.getElement().setId(composedId); + attachButton.getElement().setId(ElementIdUtils.createElementId(composedId, "attach")); //$NON-NLS-1$ + createEditButton.getElement().setId(ElementIdUtils.createElementId(composedId, "createEdit")); //$NON-NLS-1$ + } + + private void updateButtonText(InstanceImageLineModel model) { + String text = model.getDiskModel().getEntity() != null ? constants.editInstanceImages() : constants.addInstanceImages(); + createEditButton.setLabel(text); + } + + @Override + public InstanceImageLineModel flush() { + return driver.flush(); + } + + @Override + public boolean isEnabled() { + return false; + } + + @Override + public void setEnabled(boolean b) { + + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler<InstanceImageLineModel> valueChangeHandler) { + return addHandler(valueChangeHandler, ValueChangeEvent.getType()); + } + + public void setElementId(String elementId) { + this.elementId = elementId; + } +} diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImageLineEditor.ui.xml b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImageLineEditor.ui.xml new file mode 100644 index 0000000..821ddad --- /dev/null +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImageLineEditor.ui.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> +<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" + xmlns:g="urn:import:com.google.gwt.user.client.ui" + xmlns:w="urn:import:org.ovirt.engine.ui.common.widget"> + + <ui:with field='constants' type='org.ovirt.engine.ui.common.CommonApplicationConstants'/> + + <ui:style> + .mainPanel { + width: 330px; + } + + .buttonStyle { + float: right; + padding-top: 5px; + padding-right: 5px; + } + + .labelStyle { + float: left; + } + </ui:style> + + <g:FlowPanel addStyleNames="{style.mainPanel}"> + <g:Label ui:field="nameLabel" addStyleNames="{style.labelStyle}"/> + <w:UiCommandButton ui:field="createEditButton" label="{constants.addInstanceImages}" addStyleNames="{style.buttonStyle}"/> + <w:UiCommandButton ui:field="attachButton" label="{constants.attachInstanceImages}" addStyleNames="{style.buttonStyle}"/> + </g:FlowPanel> +</ui:UiBinder> \ No newline at end of file diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImagesEditor.java b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImagesEditor.java new file mode 100644 index 0000000..22c82b7 --- /dev/null +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImagesEditor.java @@ -0,0 +1,80 @@ +package org.ovirt.engine.ui.common.widget.uicommon.instanceimages; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.ui.Widget; +import org.ovirt.engine.core.common.utils.Pair; +import org.ovirt.engine.ui.common.idhandler.ElementIdHandler; +import org.ovirt.engine.ui.common.idhandler.HasElementId; +import org.ovirt.engine.ui.common.widget.AddRemoveRowWidget; +import org.ovirt.engine.ui.uicommonweb.models.vms.InstanceImageLineModel; +import org.ovirt.engine.ui.uicommonweb.models.vms.InstanceImagesModel; + + +public class InstanceImagesEditor extends AddRemoveRowWidget<InstanceImagesModel, InstanceImageLineModel, InstanceImageLineEditor> implements HasElementId { + + private InstanceImagesModel model; + + private String elementId = DOM.createUniqueId(); + + interface WidgetUiBinder extends UiBinder<Widget, InstanceImagesEditor> { + WidgetUiBinder uiBinder = GWT.create(WidgetUiBinder.class); + } + + interface WidgetIdHandler extends ElementIdHandler<InstanceImagesEditor> { + WidgetIdHandler idHandler = GWT.create(WidgetIdHandler.class); + } + + public InstanceImagesEditor() { + initWidget(WidgetUiBinder.uiBinder.createAndBindUi(this)); + WidgetIdHandler.idHandler.generateAndSetIds(this); + } + + @Override + protected InstanceImageLineEditor createWidget(InstanceImageLineModel value) { + InstanceImageLineEditor editor = new InstanceImageLineEditor(); + editor.setElementId(elementId); + editor.edit(value); + return editor; + } + + @Override + protected InstanceImageLineModel createGhostValue() { + InstanceImageLineModel lineModel = new InstanceImageLineModel(model); + // the getVm() is null on new VM - that is handled inside the line models + lineModel.initialize(null, model.getVm()); + return lineModel; + } + + @Override + protected boolean isGhost(InstanceImageLineModel value) { + return value.isGhost(); + } + + @Override + protected boolean vetoRemoveWidget(final Pair<InstanceImageLineModel, InstanceImageLineEditor> item, final InstanceImageLineModel value, final InstanceImageLineEditor widget) { + model.approveRemoveDisk(item.getFirst(), new InstanceImagesModel.RemoveApprovedCallback() { + @Override + public void removeApproved(boolean approved) { + if (approved) { + doRemoveItem(item, value, widget); + } + } + }); + + // will be eventually removed from the callback + return false; + } + + @Override + public void edit(final InstanceImagesModel model) { + this.model = model; + super.edit(model); + } + + @Override + public void setElementId(String elementId) { + this.elementId = elementId; + } +} diff --git a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImagesEditor.ui.xml b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImagesEditor.ui.xml new file mode 100644 index 0000000..abd5619 --- /dev/null +++ b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/instanceimages/InstanceImagesEditor.ui.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> +<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui"> + + <ui:with field='constants' type='org.ovirt.engine.ui.common.CommonApplicationConstants'/> + + <ui:style type="org.ovirt.engine.ui.common.widget.AddRemoveRowWidget.WidgetStyle"> + .buttonStyle { + padding-left: 2px; + padding-top: 2px; + margin-top: 6px; + margin-left: 10px; + } + + .titleStyle { + padding: 0 5px; + margin-left: 5px; + float: left; + width: 100%; + } + + .mainPanel { + width: 100%; + } + + .contentPanel { + padding-right: 10px; + margin-left: 5px; + } + + </ui:style> + + <g:FlowPanel addStyleNames="{style.mainPanel}"> + <g:Label addStyleNames="{style.titleStyle}" text="{constants.instanceImages}"/> + <g:FlowPanel ui:field="contentPanel" addStyleNames="{style.contentPanel}"/> + </g:FlowPanel> +</ui:UiBinder> \ No newline at end of file diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/configure/instancetypes/InstanceTypeListModel.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/configure/instancetypes/InstanceTypeListModel.java index f3481f8..710558b 100644 --- a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/configure/instancetypes/InstanceTypeListModel.java +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/configure/instancetypes/InstanceTypeListModel.java @@ -63,7 +63,7 @@ private final InstanceTypeInterfaceCreatingManager addInstanceTypeNetworkManager = new InstanceTypeInterfaceCreatingManager(new BaseInterfaceCreatingManager.PostVnicCreatedCallback() { @Override - public void vnicCreated(Guid vmId) { + public void vnicCreated(Guid vmId, UnitVmModel unitVmModel) { getWindow().stopProgress(); cancel(); updateActionAvailability(); @@ -311,7 +311,7 @@ return; } - UnitVmModel model = new UnitVmModel(behavior); + UnitVmModel model = new UnitVmModel(behavior, this); model.setIsAdvancedModeLocalStorageKey("instance_type_dialog"); //$NON-NLS-1$ setWindow(model); diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/InstanceImageLineModel.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/InstanceImageLineModel.java new file mode 100644 index 0000000..734c5b2 --- /dev/null +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/InstanceImageLineModel.java @@ -0,0 +1,346 @@ +package org.ovirt.engine.ui.uicommonweb.models.vms; + +import org.ovirt.engine.core.common.businessentities.Disk; +import org.ovirt.engine.core.common.businessentities.VM; +import org.ovirt.engine.core.compat.Version; +import org.ovirt.engine.ui.uicommonweb.UICommand; +import org.ovirt.engine.ui.uicommonweb.help.HelpTag; +import org.ovirt.engine.ui.uicommonweb.models.EntityModel; +import org.ovirt.engine.ui.uicompat.ConstantsManager; +import org.ovirt.engine.ui.uicompat.UIConstants; +import org.ovirt.engine.ui.uicompat.UIMessages; + +import java.util.List; + +public class InstanceImageLineModel extends EntityModel { + + public static final String CANCEL_DISK = "CancelDisk"; //$NON-NLS-1$ + + private UIMessages messages = ConstantsManager.getInstance().getMessages(); + + private UIConstants constants = ConstantsManager.getInstance().getConstants(); + + private UICommand attachCommand; + + private UICommand createEditCommand; + + private EntityModel<AbstractDiskModel> diskModel = new EntityModel<>(); + + // if the disk already exists in the engine or is just created here but not yet submitted + private boolean diskExists; + + // a hack because the diskModel from inside of this method might not have been inited when the onSave is called + private AbstractDiskModel tmpDiskModel; + + private EntityModel<String> name = new EntityModel<>(); + + private InstanceImagesModel parentModel; + + private VM vm; + + private boolean active = true; + + public InstanceImageLineModel(InstanceImagesModel parentModel) { + this.parentModel = parentModel; + + attachCommand = new UICommand("attachCommand", this); //$NON-NLS-1$ + createEditCommand = new UICommand("createEditCommand", this); //$NON-NLS-1$ + } + + private void fillData() { + if (diskModel.getEntity() == null) { + return; + } + + if (diskModel.getEntity() instanceof AttachDiskModel) { + List<EntityModel<DiskModel>> disks = ((AttachDiskModel) diskModel.getEntity()).getSelectedDisks(); + if (disks.size() != 0) { + updateName(disks.get(0).getEntity().getDisk()); + } + } else { + updateName(diskModel.getEntity().getDisk()); + } + } + + private void updateName(Disk disk) { + if (disk == null) { + return; + } + + String diskName = disk.getDiskAlias(); + String size = Long.toString(disk.getSize()); + + if (disk.getDiskStorageType() == Disk.DiskStorageType.IMAGE) { + size = Long.toString(disk.getSize() / (1024 * 1024 * 1024)); + } + + String type; + if (diskExists) { + type = constants.existingDisk(); + } else if (getDiskModel().getEntity() instanceof AttachDiskModel) { + type = constants.attachingDisk(); + } else { + type = constants.creatingDisk(); + } + + String boot = ""; //$NON-NLS-1$ + if (disk.isBoot()) { + boot = constants.bootDisk(); + } + + name.setEntity(messages.vmDialogDisk(diskName, size, type, boot)); + } + + + public void initialize(Disk disk, VM vm) { + this.vm = vm; + active = true; + diskExists = disk != null; + + attachCommand.setIsAvailable(!diskExists); + + if (disk == null) { + return; + } + + final AbstractDiskModel model = new EditDiskModel() { + @Override + public void onSave() { + if (validate()) { + flush(); + getDiskModel().setEntity(tmpDiskModel); + + // needed because the "entity" instances are the same so the event is not fired + fillData(); + + parentModel.getParentListModel().setWindow(null); + parentModel.getParentListModel().setWindow(parentModel.getUnitVmModel()); + } + } + }; + + tmpDiskModel = model; + + model.setDisk(disk); + model.setVm(vm); + + setupModelAsDialog(model, + ConstantsManager.getInstance().getConstants().editVirtualDiskTitle(), + HelpTag.edit_virtual_disk, "edit_virtual_disk"); //$NON-NLS-1$ + + model.initialize(); + diskModel.setEntity(model); + fillData(); + } + + public EntityModel<AbstractDiskModel> getDiskModel() { + return diskModel; + } + + public EntityModel<String> getName() { + return name; + } + + public void setName(EntityModel<String> name) { + this.name = name; + } + + public boolean isGhost() { + return diskModel.getEntity() == null; + } + + public void attachDisk() { + AttachDiskModel model = new AttachDiskModel() { + @Override + public void onSave() { + if (validate()) { + flush(); + List<EntityModel<DiskModel>> selectedDisks = ((AttachDiskModel) tmpDiskModel).getSelectedDisks(); + if (selectedDisks.size() == 1) { + // only 0 or 1 is allowed + tmpDiskModel.setDisk(selectedDisks.iterator().next().getEntity().getDisk()); + } + + getDiskModel().setEntity(tmpDiskModel); + parentModel.getParentListModel().setWindow(null); + parentModel.getParentListModel().setWindow(parentModel.getUnitVmModel()); + // from now on only editing is possible + attachCommand.setIsAvailable(false); + + fillData(); + } + } + }; + + tmpDiskModel = model; + VM realOrFakeVm = vm; + Version compatibilityVersion = parentModel.getUnitVmModel().getSelectedCluster().getcompatibility_version(); + if (realOrFakeVm == null) { + realOrFakeVm = new VM(); + realOrFakeVm.setId(null); + realOrFakeVm.setVdsGroupId(parentModel.getUnitVmModel().getSelectedCluster().getId()); + realOrFakeVm.setStoragePoolId(parentModel.getUnitVmModel().getSelectedDataCenter().getId()); + realOrFakeVm.setVdsGroupCompatibilityVersion(compatibilityVersion); + } + + model.setVm(realOrFakeVm); + + setupModelAsDialog(model, + ConstantsManager.getInstance().getConstants().attachVirtualDiskTitle(), + HelpTag.attach_virtual_disk, "attach_virtual_disk"); //$NON-NLS-1$ + showDialog(model); + model.initialize(parentModel.getAllCurrentDisks()); + maybeLoadAttachableDisks(model); + } + + private void maybeLoadAttachableDisks(AttachDiskModel model) { + if (model.getVm().getId() == null) { + Integer osType = parentModel.getUnitVmModel().getOSType().getSelectedItem(); + Version compatibilityVersion = parentModel.getUnitVmModel().getSelectedCluster().getcompatibility_version(); + model.loadAttachableDisks(true, osType, compatibilityVersion, getDisk()); + } else { + model.loadAttachableDisks(false, 0, null, getDisk()); + } + } + + public void createEditDisk() { + if (parentModel.getUnitVmModel().getSelectedCluster() == null || parentModel.getUnitVmModel().getSelectedDataCenter() == null) { + return; + } + + if (getDiskModel().getEntity() == null) { + showNewDialog(); + } else { + showPreviouslyShownDialog(); + } + } + + private void showPreviouslyShownDialog() { + getDiskModel().getEntity().updateBootableFrom(parentModel.getAllCurrentDisks()); + if (getDiskModel().getEntity() instanceof AttachDiskModel) { + // needed to re-filter in case the OS or the compatibility version changed + maybeLoadAttachableDisks((AttachDiskModel) getDiskModel().getEntity()); + } + showDialog(getDiskModel().getEntity()); + } + + private void showNewDialog() { + final AbstractDiskModel model = new NewDiskModel() { + @Override + public void onSave() { + if (validate()) { + flush(); + getDiskModel().setEntity(tmpDiskModel); + parentModel.getParentListModel().setWindow(null); + parentModel.getParentListModel().setWindow(parentModel.getUnitVmModel()); + // the "new" turns into "edit" - no need for attach anymore + attachCommand.setIsAvailable(false); + + fillData(); + } + } + }; + + tmpDiskModel = model; + + VM vm = new VM(); + vm.setVdsGroupId(parentModel.getUnitVmModel().getSelectedCluster().getId()); + vm.setStoragePoolId(parentModel.getUnitVmModel().getSelectedDataCenter().getId()); + vm.setVdsGroupCompatibilityVersion(parentModel.getUnitVmModel().getSelectedCluster().getcompatibility_version()); + model.setVm(vm); + + setupModelAsDialog(model, + ConstantsManager.getInstance().getConstants().newVirtualDiskTitle(), + HelpTag.new_virtual_disk, "new_virtual_disk"); //$NON-NLS-1$ + + showDialog(model); + model.initialize(parentModel.getAllCurrentDisks()); + } + + private void setupModelAsDialog(AbstractDiskModel model, String title, HelpTag helpTag, String hashName) { + model.setTitle(title); + model.setHelpTag(helpTag); + model.setHashName(hashName); + + UICommand cancelCommand = new UICommand(CANCEL_DISK, this); + cancelCommand.setTitle(ConstantsManager.getInstance().getConstants().cancel()); + cancelCommand.setIsCancel(true); + model.setCancelCommand(cancelCommand); + } + + private void showDialog(AbstractDiskModel model) { + parentModel.getParentListModel().setWindow(null); + parentModel.getParentListModel().setWindow(model); + } + + @Override + public void executeCommand(UICommand command) { + if (!active) { + // don't listen to this commands anymore - no need to show any more windows + return; + } + if (CANCEL_DISK.equals(command.getName())) { + parentModel.getParentListModel().setWindow(null); + parentModel.getParentListModel().setWindow(parentModel.getUnitVmModel()); + } else if (command == createEditCommand) { + createEditDisk(); + } else if (command == attachCommand) { + attachDisk(); + } else { + super.executeCommand(command); + } + } + + public boolean isBootable() { + if (isGhost()) { + return false; + } + + return diskModel.getEntity().getIsBootable().getEntity(); + } + + public Disk getDisk() { + AbstractDiskModel diskModel = getDiskModel().getEntity(); + + if (diskModel == null) { + return null; + } + + Disk disk = diskModel.getDisk(); + if (disk == null) { + disk = diskModel.getDiskImage(); + } + + if (disk == null) { + disk = diskModel.getLunDisk(); + } + + return disk; + } + + public boolean isDiskExists() { + return diskExists; + } + + public VM getVm() { + return vm; + } + + public UICommand getAttachCommand() { + return attachCommand; + } + + public UICommand getCreateEditCommand() { + return createEditCommand; + } + + + public void setEnabled(boolean enabled) { + attachCommand.setIsExecutionAllowed(enabled); + createEditCommand.setIsExecutionAllowed(enabled); + } + + public void deactivate() { + active = false; + } +} diff --git a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/InstanceImagesModel.java b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/InstanceImagesModel.java new file mode 100644 index 0000000..2de84fe --- /dev/null +++ b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/InstanceImagesModel.java @@ -0,0 +1,265 @@ +package org.ovirt.engine.ui.uicommonweb.models.vms; + +import org.ovirt.engine.core.common.businessentities.Disk; +import org.ovirt.engine.core.common.businessentities.VM; +import org.ovirt.engine.ui.frontend.AsyncQuery; +import org.ovirt.engine.ui.frontend.INewAsyncCallback; +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.models.ListModel; +import org.ovirt.engine.ui.uicommonweb.models.Model; +import org.ovirt.engine.ui.uicompat.FrontendActionAsyncResult; +import org.ovirt.engine.ui.uicompat.IFrontendActionAsyncCallback; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class InstanceImagesModel extends ListModel<InstanceImageLineModel> { + + private Model parentListModel; + + private UnitVmModel unitVmModel; + + private List<RemoveDiskModel> removeDiskModels = new ArrayList<>(); + + private RemoveDiskModel removeDiskModel; + + private RemoveApprovedCallback callback; + + private VM vm; + + public InstanceImagesModel(UnitVmModel unitVmModel, Model parentListModel) { + this.parentListModel = parentListModel; + this.unitVmModel = unitVmModel; + } + + public Model getParentListModel() { + return parentListModel; + } + + public UnitVmModel getUnitVmModel() { + return unitVmModel; + } + + public void approveRemoveDisk(InstanceImageLineModel lineModel, RemoveApprovedCallback callback) { + if (!lineModel.isDiskExists() || lineModel.getVm() == null) { + // no problem, the item can be removed without explicitly asking the user + callback.removeApproved(true); + return; + } + + this.callback = callback; + removeDiskModel = new RemoveDiskModel(); + + List<Disk> disksToRemove = Arrays.asList(lineModel.getDiskModel().getEntity().getDisk()); + VM vm = lineModel.getVm(); + + getParentListModel().setWindow(null); + getParentListModel().setWindow(removeDiskModel); + + removeDiskModel.initialize(vm, disksToRemove, this); + } + + private void onRemove() { + if (!removeDiskModel.validate()) { + return; + } + + removeDiskModels.add(removeDiskModel); + hideRemoveDiskAndShowEditVm(); + callback.removeApproved(true); + } + + private void onCancel() { + hideRemoveDiskAndShowEditVm(); + callback.removeApproved(false); + } + + private void hideRemoveDiskAndShowEditVm() { + getParentListModel().setWindow(null); + getParentListModel().setWindow(getUnitVmModel()); + } + + @Override + public void executeCommand(UICommand command) { + super.executeCommand(command); + + if (RemoveDiskModel.ON_REMOVE.equals(command.getName())) { + onRemove(); + } else if (RemoveDiskModel.CANCEL_REMOVE.equals(command.getName())) { + onCancel(); + } + } + + public void setVm(VM vm) { + this.vm = vm; + } + + public VM getVm() { + return vm; + } + + public static interface RemoveApprovedCallback { + void removeApproved(boolean approved); + } + + public void executeDiskModifications(VM vm) { + // this is done on the background - the window is not visible anymore + disableLineModels(); + executeDeleteAndCallNew(vm); + } + + private void disableLineModels() { + for (InstanceImageLineModel model : getItems()) { + model.deactivate(); + } + } + + private void executeDeleteAndCallNew(final VM vm) { + if (removeDiskModels.size() == 0) { + executeNewAndEdit(vm); + return; + } + + for (RemoveDiskModel removeDisk : removeDiskModels ) { + removeDisk.store(new ICommandTarget() { + @Override + public void executeCommand(UICommand command) { + executeNewAndEdit(vm); + } + + @Override + public void executeCommand(UICommand uiCommand, Object... parameters) { + executeNewAndEdit(vm); + } + }); + } + } + + private void executeNewAndEdit(final VM vm) { + if (getItems() == null) { + return; + } + + AsyncDataProvider.getInstance().getVmDiskList(new AsyncQuery(this, new INewAsyncCallback() { + @Override + public void onSuccess(Object target, Object returnValue) { + Iterator<InstanceImageLineModel> lineModelIterator = orderedDisksIterator((List<Disk>) returnValue); + storeNextDisk(lineModelIterator, vm); + } + }), vm.getId()); + + } + + /** + * Finds the disk which is boot on the VM but has been configured to be non boot. If finds such a disk, the resulting + * list will contain as a first command the one which executes this operation. + * + * It is needed because they can be other which make an another disk boot and the VM can not have more than one boot + * disk - so the validation on server would fail. + */ + private Iterator<InstanceImageLineModel> orderedDisksIterator(List<Disk> disks) { + if (disks.size() == 0) { + return getItems().iterator(); + } + + Disk previouslyBootDisk = findBoot(disks); + if (previouslyBootDisk == null) { + return getItems().iterator(); + } + + InstanceImageLineModel fromBootToNonBoot = findBecomeNonBoot(previouslyBootDisk); + if (fromBootToNonBoot == null) { + return getItems().iterator(); + } + + // now we know that the disk changed from boot to non boot so this command has to be executed as first + Set<InstanceImageLineModel> res = new LinkedHashSet<>(); + res.add(fromBootToNonBoot); + res.addAll(getItems()); + return res.iterator(); + } + + private InstanceImageLineModel findBecomeNonBoot(Disk bootDisk) { + for (InstanceImageLineModel model : getItems()) { + Disk disk = model.getDiskModel().getEntity().getDisk(); + if (disk.getId().equals(bootDisk.getId())) { + if (disk.isBoot()) { + return null; + } else { + // removed boot flag, this command has to be executed first so if other disk is marked as boot, + // it will not fail with the message that you can have only one boot + return model; + } + } + } + + return null; + } + + private Disk findBoot(List<Disk> disks) { + for (Disk disk : disks) { + if (disk.isBoot()) { + return disk; + } + } + + return null; + } + + private void storeNextDisk(final Iterator<InstanceImageLineModel> lineModelIterator, final VM vm) { + if (!lineModelIterator.hasNext()) { + return; + } + + AbstractDiskModel diskModel = lineModelIterator.next().getDiskModel().getEntity(); + + if (diskModel == null) { + storeNextDisk(lineModelIterator, vm); + } else { + diskModel.setVm(vm); + + diskModel.store(new IFrontendActionAsyncCallback() { + @Override + public void executed(FrontendActionAsyncResult result) { + // have to wait until the previous returned because the operation needs a lock on the VM + storeNextDisk(lineModelIterator, vm); + } + }); + } + + } + + public List<Disk> getAllCurrentDisks() { + List<Disk> res = new ArrayList<>(); + for (InstanceImageLineModel line : getItems()) { + if (line.isGhost()) { + continue; + } + + res.add(line.getDisk()); + } + + return res; + } + + public void updateActionsAvailability() { + boolean clusterSelected = unitVmModel.getSelectedCluster() != null; + boolean osSelected = unitVmModel.getOSType().getSelectedItem() != null; + + for (InstanceImageLineModel model : getItems()) { + if (model.isGhost()) { + continue; + } + + model.setEnabled(clusterSelected && osSelected); + } + + setIsChangable(clusterSelected && osSelected); + } +} diff --git a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java index fde7b44..3293252 100644 --- a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java +++ b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIConstants.java @@ -2461,4 +2461,16 @@ @DefaultStringValue("Click on Numa Pinning to configure VM's virtual node span on top of physical host NUMA nodes") String numaInfoMessage(); + + @DefaultStringValue("existing") + String existingDisk(); + + @DefaultStringValue("creating") + String creatingDisk(); + + @DefaultStringValue("attaching") + String attachingDisk(); + + @DefaultStringValue("boot") + String bootDisk(); } diff --git a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java index cb3329d..b40cdfd 100644 --- a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java +++ b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/UIMessages.java @@ -403,4 +403,7 @@ @DefaultMessage("Default ({0})") String defaultMtu(int mtu); + + @DefaultMessage("{0}: ({1} GB) {2} {3}") + String vmDialogDisk(String name, String sizeInGb, String type, String boot); } -- To view, visit http://gerrit.ovirt.org/36063 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I4bcaa1e40841aa12e64708ec82afdb9daec9fb7f Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Tomas Jelinek <tjeli...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches