Jakub Niedermertl has uploaded a new change for review.

Change subject: webadmin: VM Icons - frontend part
......................................................................

webadmin: VM Icons - frontend part

http://www.ovirt.org/Features/VM_Icons

Frontend part of introduction of VM icons - customizable image of VM and
template in userportal. It replaces current system of providing
operating system icons for browser and allows users to customize icons.a

* IconEditorWidget - encapsulated editor of vm icons communication with
  model using IconWithDefault class.

* 'New VM', 'Edit VM', 'Edit Template', 'New Pool' and 'Edit Pool'
  dialogs contains 'Icon' tab.

* Icons are downloaded on request in async IconVmBaseToUnitBuilder and
  cached in IconCache class.

(This patch does not contain changes in userportal.)

Change-Id: Id56aecccef6aab6604de32e27497991ab3713d8d
Bug-Url: https://bugzilla.redhat.com/1103175
Signed-off-by: Jakub Niedermertl <jnied...@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/CommonApplicationTemplates.java
M 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java
M 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java
A 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/CompositeHandlerRegistration.java
A 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.java
A 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.ui.xml
M 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java
M 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml
M 
frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java
A 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/IconCache.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java
A 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/IconVmBaseToUnitBuilder.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java
A 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/IconWithDefault.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java
A 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconValidation.java
A 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconWithDefaultValidation.java
M 
frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.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
37 files changed, 1,140 insertions(+), 47 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/01/38601/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 27c7e9d..4df4689 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
@@ -760,6 +760,27 @@
     @DefaultStringValue("Clone Name")
     String clonedVmName();
 
+    @DefaultStringValue("Icon")
+    String iconTabVmPopup();
+
+    @DefaultStringValue("New Icon")
+    String newIconVmPopup();
+
+    @DefaultStringValue("Current Icon")
+    String currentIconVmPopup();
+
+    @DefaultStringValue("Upload")
+    String uploadIconVmPopup();
+
+    @DefaultStringValue("Use default")
+    String useDefaultIconVmPopup();
+
+    @DefaultStringValue("Discard changes")
+    String discardChangesIconVmPopup();
+
+    @DefaultStringValue("Icons limitations: max dimensions: width 150px, 
heigth 120px; max size 24kB; supported formats: jpg, png, gif")
+    String iconLimitationsIconVmPopup();
+
     // Permissions
     @DefaultStringValue("Inherited Permission")
     String inheritedPermission();
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationTemplates.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationTemplates.java
index fb94750..f054bcd 100644
--- 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationTemplates.java
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/CommonApplicationTemplates.java
@@ -47,9 +47,15 @@
     @Template("<ul style='margin-top:0'>{0}</ul>")
     SafeHtml unsignedList(SafeHtml list);
 
+    @Template("<ul>{0}</ul>")
+    SafeHtml unorderedList(SafeHtml items);
+
     @Template("<li>{0}</li>")
     SafeHtml listItem(SafeHtml item);
 
+    @Template("<li>{0}</li>")
+    SafeHtml listItem(String item);
+
     @Template("{0} <sub>{1}</sub>")
     SafeHtml sub(String main, String sub);
 
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java
index 0d0c7ed..19f68cb 100644
--- 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/gin/CommonGinUiBinderWidgets.java
@@ -1,25 +1,29 @@
 package org.ovirt.engine.ui.common.gin;
 
 import com.google.gwt.inject.client.Ginjector;
+import org.ovirt.engine.ui.common.widget.editor.IconEditorWidget;
 import 
org.ovirt.engine.ui.common.widget.uicommon.popup.vm.SerialNumberPolicyWidget;
 
 /**
  * Ginjector extension containing views that make use of the GWTP GinUiBinder.
  *
  * Every view that makes use of GIN dependency injection and is to be embedded 
in a .ui.xml file --ex:
+ * <pre>
  * <code>
- *     <w:MyWidget />
+ *     &lt;w:MyWidget />
  *
  *     public class MyWidget {
- *         @Inject
+ *         {@literal @}Inject
  *         public MyWidget(ApplicationResources resources, MyOtherDependency 
dep) {
  *             // ...
  *         }
  *     }
  * </code>
+ * </pre>
  *
  * Must be registered in this interface.
  */
 public interface CommonGinUiBinderWidgets extends Ginjector {
     SerialNumberPolicyWidget getSerialNumberPolicyWidget();
+    IconEditorWidget getIconEditorWidget();
 }
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java
index df4eb9b..71512b7 100644
--- 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/HasAccess.java
@@ -2,6 +2,10 @@
 
 /**
  * Widgets that implement this interface have a user access policy associated 
with them.
+ * <p>
+ *     Inaccessible widgets should be hidden. Usually using
+ *     {@link com.google.gwt.user.client.ui.Widget#setVisible(boolean)}
+ * </p>
  */
 public interface HasAccess {
 
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/CompositeHandlerRegistration.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/CompositeHandlerRegistration.java
new file mode 100644
index 0000000..fcc8b6d
--- /dev/null
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/CompositeHandlerRegistration.java
@@ -0,0 +1,26 @@
+package org.ovirt.engine.ui.common.widget.editor;
+
+import com.google.gwt.event.shared.HandlerRegistration;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class CompositeHandlerRegistration implements HandlerRegistration {
+
+    private final List<HandlerRegistration> registrations;
+
+    private CompositeHandlerRegistration(List<HandlerRegistration> 
registrations) {
+        this.registrations = registrations;
+    }
+
+    public static CompositeHandlerRegistration of(HandlerRegistration... 
registrations) {
+        return new CompositeHandlerRegistration(Arrays.asList(registrations));
+    }
+
+    @Override
+    public void removeHandler() {
+        for(HandlerRegistration registration : registrations) {
+            registration.removeHandler();
+        }
+    }
+}
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.java
new file mode 100644
index 0000000..fb61230
--- /dev/null
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.java
@@ -0,0 +1,366 @@
+package org.ovirt.engine.ui.common.widget.editor;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.editor.client.LeafValueEditor;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.dom.client.KeyUpHandler;
+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.resources.client.CssResource;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FileUpload;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.UIObject;
+import com.google.gwt.user.client.ui.Widget;
+import org.ovirt.engine.ui.common.CommonApplicationConstants;
+import org.ovirt.engine.ui.common.CommonApplicationResources;
+import org.ovirt.engine.ui.common.CommonApplicationTemplates;
+import org.ovirt.engine.ui.common.editor.UiCommonEditor;
+import org.ovirt.engine.ui.common.widget.AbstractValidatedWidget;
+import org.ovirt.engine.ui.common.widget.dialog.InfoIcon;
+import org.ovirt.engine.ui.uicommonweb.models.vms.IconWithDefault;
+import org.ovirt.engine.ui.uicommonweb.validation.IconValidation;
+import org.ovirt.engine.ui.uicommonweb.validation.ValidationResult;
+
+import javax.inject.Inject;
+import java.util.List;
+
+/**
+ * Icon editor. It allows to set custom icon to VM-like entities.
+ */
+public class IconEditorWidget extends AbstractValidatedWidget
+        implements LeafValueEditor<IconWithDefault>,
+                   HasValueChangeHandlers<IconWithDefault>,
+                   UiCommonEditor<IconWithDefault> {
+
+    interface ViewUiBinder extends UiBinder<HTMLPanel, IconEditorWidget> {
+        ViewUiBinder uiBinder = GWT.create(ViewUiBinder.class);
+    }
+
+    protected interface Style extends CssResource {
+        String grey();
+        String iconImageDisabled();
+    }
+
+    @UiField
+    protected Style style;
+
+    @UiField
+    protected Image image;
+
+    @UiField
+    protected Button uploadButton;
+
+    @UiField(provided = true)
+    protected InfoIcon uploadInfoIcon;
+
+    @UiField
+    protected FileUpload fileUpload;
+
+    @UiField
+    protected Button defaultButton;
+
+    @UiField
+    protected HTML errorMessageHtml;
+
+    protected final CommonApplicationTemplates templates;
+
+    /**
+     * current value, the visible image <br/>
+     * in dataUri format
+     */
+    private String icon;
+
+    /**
+     * default value (given by OS of VM) <br/>
+     * in dataUri format
+     */
+    private String defaultIcon;
+
+    /**
+     * relates to {@link com.google.gwt.user.client.ui.HasEnabled} 
implementation
+     */
+    private boolean enabled;
+
+    /**
+     * relates to {@link org.ovirt.engine.ui.common.widget.HasAccess} 
implementation
+     */
+    private boolean accessible;
+
+    @Inject
+    public IconEditorWidget(CommonApplicationConstants constants,
+                            CommonApplicationTemplates templates,
+                            CommonApplicationResources resources) {
+        this.templates = templates;
+        uploadInfoIcon = new InfoIcon(
+                
SafeHtmlUtils.fromTrustedString(constants.iconLimitationsIconVmPopup()), 
resources);
+        initWidget(ViewUiBinder.uiBinder.createAndBindUi(this));
+        fileUpload.getElement().setAttribute("accept", 
"image/gif,image/jpeg,image/png"); //$NON-NLS-1$ //$NON-NLS-2$
+        fileUpload.addChangeHandler(new ChangeHandler() {
+            @Override
+            public void onChange(ChangeEvent event) {
+                IconEditorWidget.this.readUploadedIconFile();
+            }
+        });
+        fileUpload.getElement().setTabIndex(-1); // can be moved to *.ui.xml 
file in form of attribute `tabIndex="-1"` since GWT 2.7.0
+
+        KeyPressHandler preventEnterKeyPressHandler = 
createPreventEnterKeyPressHandler();
+        uploadButton.addKeyPressHandler(preventEnterKeyPressHandler);
+        defaultButton.addKeyPressHandler(preventEnterKeyPressHandler);
+
+        setEnabled(true);
+        setAccessible(true);
+    }
+
+    private KeyPressHandler createPreventEnterKeyPressHandler() {
+        return new KeyPressHandler() {
+            @Override
+            public void onKeyPress(KeyPressEvent event) {
+                if (!event.isAnyModifierKeyDown()
+                        && event.getNativeEvent().getKeyCode() == 
KeyCodes.KEY_ENTER
+                        && event.getUnicodeCharCode() == 0) {
+                    event.preventDefault();
+                }
+            }
+        };
+    }
+
+    @Override
+    public void setValue(IconWithDefault value) {
+        final IconWithDefault oldPair = getValue();
+        if (value == null) {
+            defaultIcon = null;
+            setIcon(null);
+        } else {
+            defaultIcon = value.getDefaultIcon();
+            setIcon(value.getIcon());
+        }
+        final IconWithDefault newPair = getValue();
+        ValueChangeEvent.fireIfNotEqual(this, oldPair, newPair);
+    }
+
+    @Override
+    public IconWithDefault getValue() {
+        if (icon == null || defaultButton == null) {
+            return null;
+        }
+        return new IconWithDefault(icon, defaultIcon);
+    }
+
+    @Override
+    protected Widget getValidatedWidget() {
+        return this;
+    }
+
+    @UiHandler("uploadButton")
+    void onUploadIconButton(ClickEvent event) {
+        clickElement(fileUpload.getElement());
+    }
+
+    @UiHandler("defaultButton")
+    void onDefaultIconButton(ClickEvent event) {
+        setIconAndFireChangeEvent(defaultIcon);
+    }
+
+    /**
+     * There is FileUpload#click() method since GWT 2.7.0.
+     */
+    native void clickElement(Element element) /*-{
+        element.click();
+    }-*/;
+
+    protected void setIcon(String icon) {
+        this.icon = icon;
+        if (icon != null) {
+            final ValidationResult validation = (new 
IconValidation()).validate(icon);
+            updateErrorIconLabel(validation);
+            image.getElement().setAttribute("src", icon); //$NON-NLS-1$
+        } else {
+            updateErrorIconLabel(ValidationResult.ok());
+            image.getElement().setAttribute("src", ""); //$NON-NLS-1$ 
//$NON-NLS-2$
+        }
+    }
+
+    protected void setIconAndFireChangeEvent(String icon) {
+        final IconWithDefault oldPair = getValue();
+        setIcon(icon);
+        final IconWithDefault newPair = getValue();
+        ValueChangeEvent.fireIfNotEqual(this, oldPair, newPair);
+    }
+
+    private void updateErrorIconLabel(ValidationResult validation) {
+        if (!validation.getSuccess() && validation.getReasons().isEmpty()) {
+            throw new IllegalArgumentException("Unsuccessful validation 
without any reason not allowed."); //$NON-NLS-1$
+        }
+        updateErrorIconLabel(validation.getReasons());
+    }
+
+    private void updateErrorIconLabel(List<String> reasons) {
+        if (reasons.isEmpty()) {
+            errorMessageHtml.setHTML(SafeHtmlUtils.EMPTY_SAFE_HTML);
+        } else {
+            final SafeHtml htmlReasons = toUnorderedList(reasons);
+            errorMessageHtml.setHTML(htmlReasons);
+        }
+    }
+
+    private SafeHtml toUnorderedList(List<String> stringItems) {
+        SafeHtmlBuilder builder = new SafeHtmlBuilder();
+        for (String stringItem : stringItems) {
+            builder.append(templates.listItem(stringItem));
+        }
+        return templates.unorderedList(builder.toSafeHtml());
+    }
+
+    native void readUploadedIconFile() /*-{
+        var inputFileElement = 
th...@org.ovirt.engine.ui.common.widget.editor.IconEditorWidget::fileuplo...@com.google.gwt.user.client.ui.FileUpload::getElement()();
+        var self = this;
+        var javaCallback = $entry(function (dataUri) {
+            return 
se...@org.ovirt.engine.ui.common.widget.editor.IconEditorWidget::setIconAndFireChangeEvent(Ljava/lang/String;)(dataUri);
+        });
+        if (inputFileElement.files.length > 0) {
+            var file = inputFileElement.files[0];
+            var fileReader = new FileReader();
+            fileReader.onload = onFileRead;
+            fileReader.readAsDataURL(file);
+        }
+
+        function onFileRead(event) {
+            var iconDataUri = event.target.result;
+            javaCallback(iconDataUri);
+        }
+    }-*/;
+
+
+    /*
+     * see com.google.gwt.user.client.ui.ValueListBox.addValueChangeHandler()
+     */
+    @Override
+    public HandlerRegistration 
addValueChangeHandler(ValueChangeHandler<IconWithDefault> handler) {
+        return this.addHandler(handler, ValueChangeEvent.getType());
+    }
+
+    @Override
+    public void markAsValid() {
+        super.markAsValid();
+        getValidatedWidgetStyle().setBorderColor("transparent"); //$NON-NLS-1$
+    }
+
+    @Override
+    public void markAsInvalid(List<String> validationHints) {
+        super.markAsInvalid(validationHints);
+        updateErrorIconLabel(validationHints);
+    }
+
+    @Override
+    public LeafValueEditor<IconWithDefault> getActualEditor() {
+        return this;
+    }
+
+    @Override
+    public int getTabIndex() {
+        return uploadButton.getTabIndex();
+    }
+
+    @Override
+    public void setAccessKey(char key) {
+        uploadButton.setAccessKey(key);
+    }
+
+    @Override
+    public void setFocus(boolean focused) {
+        uploadButton.setFocus(focused);
+    }
+
+    @Override
+    public void setTabIndex(int index) {
+        uploadButton.setTabIndex(index);
+        defaultButton.setTabIndex(index);
+    }
+
+    @Override
+    public void disable(String disabilityHint) {
+        setEnabled(false, disabilityHint);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        setEnabled(enabled, ""); //$NON-NLS-1$
+    }
+
+    protected void setEnabled(boolean enabled, String hint) {
+        this.enabled = enabled;
+        uploadButton.setEnabled(enabled);
+        uploadButton.setTitle(hint);
+        defaultButton.setEnabled(enabled);
+        defaultButton.setTitle(hint);
+        ensureStyleNamePresent(errorMessageHtml, !enabled, style.grey());
+        errorMessageHtml.setTitle(hint);
+        ensureStyleNamePresent(image, !enabled, style.iconImageDisabled());
+        image.setTitle(hint);
+    }
+
+    private static void ensureStyleNamePresent(UIObject object, boolean 
styleNameExists, String styleName) {
+        if (styleNameExists) {
+            object.addStyleName(styleName);
+        } else {
+            object.removeStyleName(styleName);
+        }
+    }
+
+    @Override
+    public boolean isAccessible() {
+        return accessible;
+    }
+
+    @Override
+    public void setAccessible(boolean accessible) {
+        this.accessible = accessible;
+        setVisible(accessible);
+    }
+
+    @Override
+    public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
+        return CompositeHandlerRegistration.of(
+                uploadButton.addKeyDownHandler(handler),
+                defaultButton.addKeyDownHandler(handler));
+    }
+
+    @Override
+    public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
+        return CompositeHandlerRegistration.of(
+                uploadButton.addKeyPressHandler(handler),
+                defaultButton.addKeyPressHandler(handler));
+    }
+
+    @Override
+    public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
+        return CompositeHandlerRegistration.of(
+                uploadButton.addKeyUpHandler(handler),
+                defaultButton.addKeyUpHandler(handler));
+    }
+
+
+}
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.ui.xml
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.ui.xml
new file mode 100644
index 0000000..ec7142f
--- /dev/null
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/editor/IconEditorWidget.ui.xml
@@ -0,0 +1,90 @@
+<?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:d="urn:import:org.ovirt.engine.ui.common.widget.dialog">
+
+    <ui:with field='resources' 
type='org.ovirt.engine.ui.common.CommonApplicationResources'/>
+    <ui:with field='constants' 
type='org.ovirt.engine.ui.common.CommonApplicationConstants'/>
+
+    <ui:style 
type="org.ovirt.engine.ui.common.widget.editor.IconEditorWidget.Style">
+
+        .iconImage {
+            display: block;
+            height: 120px;
+            width: 150px;
+            border: thin solid rgb(211, 211, 211);
+        }
+
+        .iconImageDisabled {
+            opacity: 0.4;
+        }
+
+        .horizontal-spacing > tbody > tr > td {
+            padding-left: 10px;
+        }
+
+        .inline-block {
+            display: inline-block
+        }
+
+        .iconButton {
+            margin: 0px 0px 10px;
+            min-width: 110px;
+        }
+
+        .iconButton:focus {
+            outline: thin black dotted;
+        }
+
+        .hidden {
+            display: none;
+        }
+
+        .grey {
+            color: grey;
+        }
+
+        .iconInfoIcon {
+            margin: 0px 7px;
+        }
+
+        .iconErrorHtml {
+            position: relative;
+            bottom: 0px;
+            left: 0px;
+            margin: 10px;
+        }
+
+        .iconErrorHtml ul {
+            padding-left: 13px;
+        }
+
+        .no-border {
+            border: none;
+        }
+
+    </ui:style>
+
+
+    <g:HTMLPanel addStyleNames="{style.no-border}">
+        <g:HorizontalPanel addStyleNames="{style.horizontal-spacing}">
+            <g:Image ui:field="image" addStyleNames="{style.iconImage}"/>
+            <g:VerticalPanel>
+                <g:FlowPanel>
+                    <g:Button ui:field="uploadButton" 
text="{constants.uploadIconVmPopup}"
+                              addStyleNames="{style.inline-block} 
{style.iconButton}"/>
+                    <d:InfoIcon ui:field="uploadInfoIcon"
+                                addStyleNames="{style.inline-block} 
{style.iconInfoIcon}"/>
+                    <g:HTMLPanel addStyleNames="{style.hidden}">
+                        <g:FileUpload ui:field="fileUpload"/>
+                    </g:HTMLPanel>
+                </g:FlowPanel>
+                <g:Button ui:field="defaultButton" 
text="{constants.useDefaultIconVmPopup}"
+                          addStyleNames="{style.iconButton}"/>
+            </g:VerticalPanel>
+        </g:HorizontalPanel>
+        <g:HTML ui:field="errorMessageHtml" 
addStyleNames="{style.iconErrorHtml}"/>
+    </g:HTMLPanel>
+
+</ui:UiBinder>
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java
index 2732ef2..c17661f 100644
--- 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.java
@@ -28,6 +28,7 @@
 import com.google.gwt.user.client.ui.RadioButton;
 import com.google.gwt.user.client.ui.ValueLabel;
 import com.google.gwt.user.client.ui.Widget;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -72,6 +73,7 @@
 import org.ovirt.engine.ui.common.widget.dialog.tab.DialogTab;
 import org.ovirt.engine.ui.common.widget.dialog.tab.DialogTabPanel;
 import org.ovirt.engine.ui.common.widget.editor.EntityModelCellTable;
+import org.ovirt.engine.ui.common.widget.editor.IconEditorWidget;
 import org.ovirt.engine.ui.common.widget.editor.ListModelListBoxEditor;
 import org.ovirt.engine.ui.common.widget.editor.ListModelListBoxOnlyEditor;
 import 
org.ovirt.engine.ui.common.widget.editor.ListModelTypeAheadChangeableListBoxEditor;
@@ -871,6 +873,15 @@
     @Ignore
     protected DialogTabPanel mainTabPanel;
 
+    // ==Icon Tab==
+    @UiField
+    @Ignore
+    protected DialogTab iconTab;
+
+    @UiField
+    @Path("icon.entity")
+    protected IconEditorWidget iconEditorWidget;
+
     private UnitVmModel unitVmModel;
 
     private final Driver driver = GWT.create(Driver.class);
@@ -1000,6 +1011,7 @@
         getTabNameMapping().put(TabName.POOL_TAB, this.poolTab);
         getTabNameMapping().put(TabName.RESOURCE_ALLOCATION_TAB, 
this.resourceAllocationTab);
         getTabNameMapping().put(TabName.SYSTEM_TAB, this.systemTab);
+        getTabNameMapping().put(TabName.ICON_TAB, this.iconTab);
     }
 
     private void initDetachableFields() {
@@ -1508,6 +1520,9 @@
         numOfSocketsEditorWithDetachable.setLabel(constants.numOfSockets());
         emulatedMachine.setLabel(constants.emulatedMachineLabel());
         customCpu.setLabel(constants.cpuModelLabel());
+
+        // Icon tab
+        iconTab.setLabel(constants.iconTabVmPopup());
     }
 
     protected void applyStyles() {
@@ -2012,6 +2027,10 @@
         // ==Custom Properties Tab==
         nextTabIndex = customPropertiesTab.setTabIndexes(nextTabIndex);
 
+        // ==Icon Tab==
+        nextTabIndex = iconTab.setTabIndexes(nextTabIndex);
+        iconEditorWidget.setTabIndex(nextTabIndex++);
+
         return nextTabIndex;
     }
 
@@ -2072,7 +2091,8 @@
                 rngDeviceTab,
                 highAvailabilityTab,
                 poolTab,
-                systemTab);
+                systemTab,
+                iconTab);
     }
 
     protected List<Widget> adancedFieldsFromGeneralTab() {
@@ -2128,7 +2148,8 @@
                 highAvailabilityTab,
                 resourceAllocationTab,
                 customPropertiesTab,
-                rngDeviceTab
+                rngDeviceTab,
+                iconTab
         );
     }
 
@@ -2160,7 +2181,8 @@
             bootOptionsTab,
             customPropertiesTab,
             systemTab,
-            rngDeviceTab
+            rngDeviceTab,
+            iconTab
         );
     }
 
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml
index 00894e6..724fba3 100644
--- 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/AbstractVmPopupWidget.ui.xml
@@ -426,6 +426,42 @@
         .overrideMigrationDowntimeContentEditor {
             width: 20px;
         }
+
+        .iconImage {
+            height: 120px;
+            width: 150px;
+            border: thin solid rgb(211, 211, 211);
+            display: block;
+        }
+
+        .horizontal-spacing > tbody > tr > td {
+            margin-left: 10px;
+        }
+
+        .inline-block {
+            display: inline-block
+        }
+
+        .iconButton {
+            margin: 0px 0px 5px;
+            min-width: 110px;
+        }
+
+        .hidden {
+            display: none;
+        }
+
+        .iconInfoIcon {
+            margin: 0px 7px;
+        }
+
+        .iconErrorLabel {
+            position: relative;
+            bottom: 0px;
+            left: 0px;
+            margin: 10px;
+        }
+
     </ui:style>
 
     <t:DialogTabPanel width="100%" height="100%" ui:field="mainTabPanel">
@@ -787,6 +823,13 @@
                 </t:content>
             </t:DialogTab>
         </t:tab>
+        <t:tab>
+            <t:DialogTab ui:field="iconTab">
+                <t:content>
+                    <e:IconEditorWidget ui:field="iconEditorWidget" />
+                </t:content>
+            </t:DialogTab>
+        </t:tab>
     </t:DialogTabPanel>
 
 </ui:UiBinder>
diff --git 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java
 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java
index 8b7beea..3c44d1a 100644
--- 
a/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java
+++ 
b/frontend/webadmin/modules/gwt-common/src/main/java/org/ovirt/engine/ui/common/widget/uicommon/popup/instancetypes/InstanceTypesPopupWidget.java
@@ -70,6 +70,7 @@
                 putOne(timeZoneEditorWithInfo, hiddenField()).
                 putOne(startRunningOnPanel, hiddenField()).
                 putOne(spiceCopyPasteEnabledEditor, hiddenField()).
-                putOne(spiceFileTransferEnabledEditor, hiddenField());
+                putOne(spiceFileTransferEnabledEditor, hiddenField()).
+                putOne(iconTab, hiddenField());
     }
 }
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java
index 45d3365..0ee65be 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Cloner.java
@@ -381,7 +381,6 @@
         obj.setVmPoolType(instance.getVmPoolType());
         obj.setVdsGroupId(instance.getVdsGroupId());
 
-        obj.setVmPoolType(instance.getVmPoolType());
         obj.setParameters(instance.getParameters());
         obj.setDefaultEndTime(instance.getDefaultEndTime());
         obj.setDefaultStartTime(instance.getDefaultStartTime());
@@ -465,6 +464,7 @@
         obj.setPredefinedProperties(instance.getPredefinedProperties());
         obj.setUserDefinedProperties(instance.getUserDefinedProperties());
         obj.setCustomProperties(instance.getCustomProperties());
+        obj.setIconId(instance.getIconId());
 
         return obj;
     }
@@ -514,6 +514,7 @@
         obj.setPredefinedProperties(instance.getPredefinedProperties());
         obj.setUserDefinedProperties(instance.getUserDefinedProperties());
         obj.setCustomProperties(instance.getCustomProperties());
+        obj.setIconId(instance.getIconId());
 
         return obj;
     }
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/IconCache.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/IconCache.java
new file mode 100644
index 0000000..5918c94
--- /dev/null
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/IconCache.java
@@ -0,0 +1,162 @@
+package org.ovirt.engine.ui.uicommonweb;
+
+import org.ovirt.engine.core.common.queries.GetVmIconsParameters;
+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.ui.frontend.AsyncQuery;
+import org.ovirt.engine.ui.frontend.Frontend;
+import org.ovirt.engine.ui.frontend.INewAsyncCallback;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Bidirectional map Guid &lt;-> String (icon id &lt;-> dataUri icon)
+ */
+public class IconCache {
+
+    public static final Guid DEFAULT_ICON_ID = Guid.Empty;
+    private static IconCache instance;
+    private GuidIconBiDiMap cache = new GuidIconBiDiMap();
+
+    private IconCache() {
+    }
+
+    public static IconCache getInstance() {
+        if (instance == null) {
+            instance = new IconCache();
+        }
+        return instance;
+    }
+
+
+    /**
+     * @param iconIds id of requested icon, should not be null, nor the item 
should be null
+     * @param callback callback that receives the icon; The callback can be 
called both synchronously
+     *                 (in case the icon is cached locally) and asynchronously 
(if a query to server
+     *                 needs to be done)
+     */
+    public void getOrFetchIcons(final List<Guid> iconIds, final IconsCallback 
callback) {
+        assertNotNull(iconIds);
+        final Map<Guid, String> localResult = getIcons(iconIds);
+        if (localResult != null) {
+            callback.onSuccess(localResult);
+        } else {
+            Frontend.getInstance().runQuery(VdcQueryType.GetVmIcons, 
GetVmIconsParameters.getIconsParameters(iconIds),
+                    new AsyncQuery(null, new INewAsyncCallback() {
+                        @Override
+                        public void onSuccess(Object nothing, Object 
returnValue) {
+                            Map<Guid, String> idToIconMap =
+                                    ((VdcQueryReturnValue) 
returnValue).getReturnValue();
+                            IconCache.this.cache.putAll(idToIconMap);
+                            final Map<Guid, String> result = 
IconCache.this.getIcons(iconIds);
+                            callback.onSuccess(result);
+                        }
+                    })
+            );
+        }
+
+    }
+
+    /**
+     * @return cached icon or null if icon of given id is not cached
+     */
+    public String getIcon(Guid iconId) {
+        return cache.getIcon(iconId);
+    }
+
+    /**
+     * Sugar for {@link #getOrFetchIcons(List, IconCache.IconsCallback)}
+     */
+    public void getOrFetchIcon(final Guid iconId, final IconCallback callback) 
{
+        getOrFetchIcons(Collections.singletonList(iconId), new IconsCallback() 
{
+            @Override
+            public void onSuccess(Map<Guid, String> idToIconMap) {
+                final String icon = idToIconMap.get(iconId);
+                callback.onSuccess(icon);
+            }
+        });
+    }
+
+    private void assertNotNull(List list) {
+        if (list == null) {
+            throw new IllegalArgumentException("Argument should not be 
null."); //$NON-NLS-1$
+        }
+        for (Object item : list) {
+            if (item == null) {
+                throw new IllegalArgumentException("Argument should not 
contain null."); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /**
+     * If the nullableId is {@code null} it return id of default icon,
+     * otherwise it behaves like identity function.
+     * @return never null
+     */
+    public static Guid defaultIdIfNull(Guid nullableId) {
+        if (nullableId == null) {
+            return DEFAULT_ICON_ID;
+        }
+        return nullableId;
+    }
+
+    /**
+     * @param iconIds requested icon ids
+     * @return icon ids -> icon data uri
+     */
+    private Map<Guid, String> getIcons(List<Guid> iconIds) {
+        Map<Guid, String> result = new HashMap<>();
+        for (Guid iconId : iconIds) {
+            final String cachedIcon = cache.getIcon(iconId);
+            if (cachedIcon == null) {
+                return null;
+            }
+            result.put(iconId, cachedIcon);
+        }
+        return result;
+    }
+
+    public Guid getId(String icon) {
+        return cache.getId(icon);
+    }
+
+    public static interface IconsCallback {
+        public void onSuccess(Map<Guid, String> idToIconMap);
+    }
+
+    public static interface IconCallback {
+        public void onSuccess(String icon);
+    }
+
+    private static class GuidIconBiDiMap {
+
+        private Map<Guid, String> map = new HashMap<>();
+        private Map<String, Guid> reverseMap = new HashMap<>();
+
+        public String getIcon(Guid id) {
+            return map.get(id);
+        }
+
+        public Guid getId(String icon) {
+            return reverseMap.get(icon);
+        }
+
+        public void put(Guid id, String icon) {
+            if (id == null || icon == null) {
+                throw new IllegalArgumentException("Neither 'id' nor 'icon' 
can be null."); //$NON-NLS-1$
+            }
+            map.put(id, icon);
+            reverseMap.put(icon, id);
+        }
+
+        public void putAll(Map<Guid, String> idToIconMap) {
+            for (Map.Entry<Guid, String> entry : idToIconMap.entrySet()) {
+                put(entry.getKey(), entry.getValue());
+            }
+        }
+    }
+}
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java
index a6d2ae0..b354daf 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/Linq.java
@@ -798,6 +798,10 @@
         return result;
     }
 
+    public static <T> List<T> concatTypesafe(List<T>... lists) {
+        return concat(lists);
+    }
+
     public static <T> ArrayList<T> union(ArrayList<ArrayList<T>> lists)
     {
         HashSet<T> set = new HashSet<T>();
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java
index a08a737..0f00d98 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreUnitToVmBaseBuilder.java
@@ -1,6 +1,7 @@
 package org.ovirt.engine.ui.uicommonweb.builders.vm;
 
 import org.ovirt.engine.core.common.businessentities.VmBase;
+import org.ovirt.engine.ui.uicommonweb.IconCache;
 import org.ovirt.engine.ui.uicommonweb.models.vms.UnitVmModel;
 
 /**
@@ -26,6 +27,7 @@
         vm.setIsoPath(model.getCdImage().getIsChangable() ? 
model.getCdImage().getSelectedItem() : ""); //$NON-NLS-1$
         vm.setDeleteProtected(model.getIsDeleteProtected().getEntity());
         vm.setOsId(model.getOSType().getSelectedItem());
+        
vm.setIconId(IconCache.getInstance().getId(model.getIcon().getEntity().getIcon()));
         
vm.setVncKeyboardLayout(model.getVncKeyboardLayout().getSelectedItem());
         
vm.setSerialNumberPolicy(model.getSerialNumberPolicy().getSelectedSerialNumberPolicy());
         
vm.setCustomSerialNumber(model.getSerialNumberPolicy().getCustomSerialNumber().getEntity());
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java
index 8410bd8..77fa6d1 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/CoreVmBaseToUnitBuilder.java
@@ -8,7 +8,8 @@
     public CoreVmBaseToUnitBuilder() {
         super(
                 new KernelParamsVmBaseToUnitBuilder(),
-                new SerialNumberPolicyVmBaseToUnitBuilder()
+                new SerialNumberPolicyVmBaseToUnitBuilder(),
+                new IconVmBaseToUnitBuilder()
         );
     }
 
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/IconVmBaseToUnitBuilder.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/IconVmBaseToUnitBuilder.java
new file mode 100644
index 0000000..8da2160
--- /dev/null
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/builders/vm/IconVmBaseToUnitBuilder.java
@@ -0,0 +1,32 @@
+package org.ovirt.engine.ui.uicommonweb.builders.vm;
+
+import org.ovirt.engine.core.common.businessentities.VmBase;
+import org.ovirt.engine.core.compat.Guid;
+import org.ovirt.engine.ui.uicommonweb.IconCache;
+import org.ovirt.engine.ui.uicommonweb.builders.Builder;
+import org.ovirt.engine.ui.uicommonweb.builders.BuilderList;
+import org.ovirt.engine.ui.uicommonweb.dataprovider.AsyncDataProvider;
+import org.ovirt.engine.ui.uicommonweb.models.vms.IconWithDefault;
+import org.ovirt.engine.ui.uicommonweb.models.vms.UnitVmModel;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public class IconVmBaseToUnitBuilder implements Builder<VmBase, UnitVmModel> {
+
+    @Override
+    public void build(final VmBase source, final UnitVmModel destination, 
final BuilderList<VmBase, UnitVmModel> rest) {
+        final Guid defaultIconId = 
AsyncDataProvider.getInstance().getDefaultIconId(source.getOsId());
+        final Guid iconId = IconCache.defaultIdIfNull(source.getIconId());
+        IconCache.getInstance().getOrFetchIcons(Arrays.asList(iconId, 
defaultIconId), new IconCache.IconsCallback() {
+            @Override
+            public void onSuccess(Map<Guid, String> idToIconMap) {
+                destination.getIcon().setEntity(new IconWithDefault(
+                        idToIconMap.get(iconId),
+                        idToIconMap.get(defaultIconId)
+                ));
+                rest.head().build(source, destination, rest.tail());
+            }
+        });
+    }
+}
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java
index 1bcdc19..2e1d99c 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/dataprovider/AsyncDataProvider.java
@@ -119,6 +119,7 @@
 import org.ovirt.engine.core.common.queries.GetTagsByVdsIdParameters;
 import org.ovirt.engine.core.common.queries.GetTagsByVmIdParameters;
 import 
org.ovirt.engine.core.common.queries.GetVmChangedFieldsForNextRunParameters;
+import org.ovirt.engine.core.common.queries.GetVmIconsParameters;
 import org.ovirt.engine.core.common.queries.GetVmTemplateParameters;
 import org.ovirt.engine.core.common.queries.IdQueryParameters;
 import org.ovirt.engine.core.common.queries.InterfaceAndIdQueryParameters;
@@ -160,6 +161,7 @@
 import org.ovirt.engine.ui.frontend.Frontend;
 import org.ovirt.engine.ui.frontend.IAsyncConverter;
 import org.ovirt.engine.ui.frontend.INewAsyncCallback;
+import org.ovirt.engine.ui.uicommonweb.IconCache;
 import org.ovirt.engine.ui.uicommonweb.Linq;
 import org.ovirt.engine.ui.uicommonweb.models.ApplicationModeHelper;
 import org.ovirt.engine.ui.uicommonweb.models.EntityModel;
@@ -211,6 +213,9 @@
 
     // cached OS names
     private HashMap<Integer, String> osNames;
+
+    // OS default icons
+    private Map<Integer, Guid> osIdToDefaultIconIdMap;
 
     // cached list of os ids
     private List<Integer> osIds;
@@ -285,6 +290,7 @@
             }
         }));
         initOsNames();
+        initOsDefaultIconIds();
         initUniqueOsNames();
         initLinuxOsTypes();
         initWindowsOsTypes();
@@ -3435,6 +3441,17 @@
         Frontend.getInstance().runQuery(VdcQueryType.OsRepository, new 
OsQueryParameters(OsRepositoryVerb.GetOsNames), callback);
     }
 
+    private void initOsDefaultIconIds() {
+        AsyncQuery callback = new AsyncQuery();
+        callback.asyncCallback = new INewAsyncCallback() {
+            @Override
+            public void onSuccess(Object model, Object returnValue) {
+                osIdToDefaultIconIdMap = ((VdcQueryReturnValue) 
returnValue).getReturnValue();
+            }
+        };
+        Frontend.getInstance().runQuery(VdcQueryType.GetVmIcons, 
GetVmIconsParameters.getDefaultIconsParameters(), callback);
+    }
+
     private void initOsIds() {
         osIds = new ArrayList<Integer>(osNames.keySet());
         Collections.sort(osIds, new Comparator<Integer>() {
@@ -3469,6 +3486,18 @@
         return osNames.get(osId);
     }
 
+    /**
+     * If <code>osId == null</code> then {@link IconCache#DEFAULT_ICON_ID} is 
returned
+     * otherwise Guid of default icon for given OS is returned.
+     */
+    public Guid getDefaultIconId(Integer osId) {
+        if (osId == null) {
+            return IconCache.DEFAULT_ICON_ID;
+        }
+        final Guid defaultIconIdFromDb = osIdToDefaultIconIdMap.get(osId);
+        return defaultIconIdFromDb != null ? defaultIconIdFromDb : 
IconCache.DEFAULT_ICON_ID;
+    }
+
     public boolean hasSpiceSupport(int osId, Version version) {
         for (Pair<GraphicsType, DisplayType> graphicsDisplayPair : 
getGraphicsAndDisplays(osId, version)) {
             if (graphicsDisplayPair.getFirst() == GraphicsType.SPICE) {
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java
index ccbd4d9..02e81b1 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/TabName.java
@@ -24,6 +24,7 @@
     RESOURCE_ALLOCATION_TAB,
     SYSTEM_TAB,
     TAB_RNG,
-    FIRST_RUN;
+    FIRST_RUN,
+    ICON_TAB;
 
 }
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java
index 35fa9a3..8da8fb1 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/pools/PoolListModel.java
@@ -467,6 +467,9 @@
 
                         if (model.getIsNew())
                         {
+                            if (model.getIcon().getEntity().isCustom()) {
+                                
param.setVmIcon(model.getIcon().getEntity().getIcon());
+                            }
                             
Frontend.getInstance().runMultipleAction(VdcActionType.AddVmPoolWithVms,
                                     new 
ArrayList<VdcActionParametersBase>(Arrays.asList(new VdcActionParametersBase[] 
{ param })),
                                     new IFrontendMultipleActionAsyncCallback() 
{
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java
index 2939b97..8b98919 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/TemplateListModel.java
@@ -30,6 +30,7 @@
 import org.ovirt.engine.ui.frontend.Frontend;
 import org.ovirt.engine.ui.frontend.INewAsyncCallback;
 import org.ovirt.engine.ui.uicommonweb.Cloner;
+import org.ovirt.engine.ui.uicommonweb.IconCache;
 import org.ovirt.engine.ui.uicommonweb.Linq;
 import org.ovirt.engine.ui.uicommonweb.UICommand;
 import org.ovirt.engine.ui.uicommonweb.builders.BuilderExecutor;
@@ -633,6 +634,11 @@
         VmTemplate selectedItem = ((TemplateVmModelBehavior) 
model.getBehavior()).getVmTemplate();
         final VmTemplate template = (VmTemplate) Cloner.clone(selectedItem);
 
+        final String iconForParameters = 
IconCache.getInstance().getIcon(selectedItem.getIconId()).equals(
+                model.getIcon().getEntity().getIcon())
+                ? null
+                : model.getIcon().getEntity().getIcon();
+
         String name = model.getName().getEntity();
 
         // Check name unicitate.
@@ -654,6 +660,7 @@
 
         
template.setVmInit(model.getVmInitModel().buildCloudInitParameters(model));
         UpdateVmTemplateParameters parameters = new 
UpdateVmTemplateParameters(template);
+        parameters.setVmIcon(iconForParameters);
         
parameters.setConsoleEnabled(model.getIsConsoleDeviceEnabled().getEntity());
         setVmWatchdogToParams(model, parameters);
         BuilderExecutor.build(model, parameters, new 
UnitToGraphicsDeviceParamsBuilder());
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java
index 5953988..af3fda0 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/templates/VmBaseListModel.java
@@ -378,6 +378,7 @@
         
parameters.setCopyTemplatePermissions(model.getCopyPermissions().getEntity());
         
parameters.setSoundDeviceEnabled(model.getIsSoundcardEnabled().getEntity());
         
parameters.setVirtioScsiEnabled(model.getIsVirtioScsiEnabled().getEntity());
+        parameters.setVmIcon(model.getIcon().getEntity().getIcon());
         setVmWatchdogToParams(model, parameters);
         setRngDeviceToParams(model, parameters);
         BuilderExecutor.build(model, parameters, new 
UnitToGraphicsDeviceParamsBuilder());
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java
index 410cb26..0592929 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/ExistingVmModelBehavior.java
@@ -302,6 +302,7 @@
 
     @Override
     public void oSType_SelectedItemChanged() {
+        super.oSType_SelectedItemChanged();
         Integer osType = getModel().getOSType().getSelectedItem();
 
         if (osType != null) {
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/IconWithDefault.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/IconWithDefault.java
new file mode 100644
index 0000000..598de12
--- /dev/null
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/IconWithDefault.java
@@ -0,0 +1,69 @@
+package org.ovirt.engine.ui.uicommonweb.models.vms;
+
+/**
+ * Bean holding two icons in dataUri format.
+ * <br/>
+ * {@link #icon} - the visible, selected icon
+ * <br/>
+ * {@link #defaultIcon} - icon of currently selected os
+ */
+public class IconWithDefault {
+
+    /**
+     * Icon - this value changes over time during model-view communication
+     */
+    private final String icon;
+
+    /**
+     * Default (VM OS based) icon. It allows user to reset custom icon 
settings.
+     */
+    private final String defaultIcon;
+
+    public IconWithDefault(String icon, String defaultIcon) {
+        if (icon == null || defaultIcon == null) {
+            throw new IllegalArgumentException("Both arguments should not be 
null."); //$NON-NLS-1$
+        }
+        this.icon = icon;
+        this.defaultIcon = defaultIcon;
+    }
+
+    public String getIcon() {
+        return icon;
+    }
+
+    public String getDefaultIcon() {
+        return defaultIcon;
+    }
+
+    public boolean isCustom() {
+        return !getIcon().equals(getDefaultIcon());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof IconWithDefault)) return false;
+
+        IconWithDefault that = (IconWithDefault) o;
+
+        if (defaultIcon != null ? !defaultIcon.equals(that.defaultIcon) : 
that.defaultIcon != null) return false;
+        if (icon != null ? !icon.equals(that.icon) : that.icon != null) return 
false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = icon != null ? icon.hashCode() : 0;
+        result = 31 * result + (defaultIcon != null ? defaultIcon.hashCode() : 
0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "IconWithDefault{" + //$NON-NLS-1$
+                "icon='" + (icon == null ? "null" : (icon.substring(0, 30) + 
"…'")) + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                ", defaultIcon='" + ((defaultIcon == null) ? "null" : 
defaultIcon.substring(0, 30)) + "'" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                '}';
+    }
+}
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java
index 688bb5f..16c4274 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewTemplateVmModelBehavior.java
@@ -288,11 +288,6 @@
     }
 
     @Override
-    public void oSType_SelectedItemChanged()
-    {
-    }
-
-    @Override
     public void updateMinAllocatedMemory()
     {
     }
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java
index 1ef19db..cdf76cc 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/NewVmModelBehavior.java
@@ -34,7 +34,6 @@
     public void initialize(SystemTreeItemModel systemTreeSelectedItem)
     {
         super.initialize(systemTreeSelectedItem);
-
         getModel().getIsSoundcardEnabled().setIsChangable(true);
         getModel().getVmType().setIsChangable(true);
         getModel().getVmId().setIsAvailable(true);
@@ -115,8 +114,7 @@
 
                 updateTimeZone(template.getTimeZone());
 
-                if (!template.getId().equals(Guid.Empty))
-                {
+                if (!template.getId().equals(Guid.Empty)) {
                     getModel().getStorageDomain().setIsChangable(true);
                     getModel().getProvisioning().setIsChangable(true);
 
@@ -125,9 +123,7 @@
                     
getModel().getAllowConsoleReconnect().setEntity(template.isAllowConsoleReconnect());
                     initDisks();
                     updateRngDevice(template.getId());
-                }
-                else
-                {
+                } else {
                     getModel().getStorageDomain().setIsChangable(false);
                     getModel().getProvisioning().setIsChangable(false);
 
@@ -220,6 +216,7 @@
 
     @Override
     public void oSType_SelectedItemChanged() {
+        super.oSType_SelectedItemChanged();
         VmTemplate template = 
getModel().getTemplateWithVersion().getSelectedItem() == null
                 ? null
                 : 
getModel().getTemplateWithVersion().getSelectedItem().getTemplateVersion();
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java
index ba1fd21..5bffdf9 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/PoolModelBehaviorBase.java
@@ -203,6 +203,7 @@
 
     @Override
     public void oSType_SelectedItemChanged() {
+        super.oSType_SelectedItemChanged();
         VmTemplate template = 
getModel().getTemplateWithVersion().getSelectedItem() == null
                 ? null
                 : 
getModel().getTemplateWithVersion().getSelectedItem().getTemplateVersion();
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java
index 6b86ccf..79a520a 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/TemplateVmModelBehavior.java
@@ -165,11 +165,6 @@
     }
 
     @Override
-    public void oSType_SelectedItemChanged()
-    {
-    }
-
-    @Override
     public void updateMinAllocatedMemory()
     {
     }
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java
index 430a13f..a8ee1fd 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/UnitVmModel.java
@@ -44,6 +44,7 @@
 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.IconCache;
 import org.ovirt.engine.ui.uicommonweb.Linq;
 import org.ovirt.engine.ui.uicommonweb.TypeResolver;
 import org.ovirt.engine.ui.uicommonweb.UICommand;
@@ -68,6 +69,7 @@
 import 
org.ovirt.engine.ui.uicommonweb.validation.I18NExtraNameOrNoneValidation;
 import org.ovirt.engine.ui.uicommonweb.validation.I18NNameValidation;
 import org.ovirt.engine.ui.uicommonweb.validation.IValidation;
+import org.ovirt.engine.ui.uicommonweb.validation.IconWithDefaultValidation;
 import org.ovirt.engine.ui.uicommonweb.validation.IntegerValidation;
 import org.ovirt.engine.ui.uicommonweb.validation.LengthValidation;
 import 
org.ovirt.engine.ui.uicommonweb.validation.NoTrimmingWhitespacesValidation;
@@ -202,6 +204,19 @@
     }
 
     /**
+     * VM icon
+     */
+    private NotChangableForVmInPoolEntityModel<IconWithDefault> icon;
+
+    public EntityModel<IconWithDefault> getIcon() {
+        return icon;
+    }
+
+    public void setIcon(NotChangableForVmInPoolEntityModel<IconWithDefault> 
icon) {
+        this.icon = icon;
+    }
+
+    /**
      * Note: We assume that this method is called only once, on the creation 
stage of the model. if this assumption is
      * changed (i.e the VM can attached/detached from a pool after the model 
is created), this method should be modified
      */
@@ -285,6 +300,9 @@
             // ==Custom Properties Tab==
             getCustomProperties().setIsChangable(false);
             getCustomPropertySheet().setIsChangable(false);
+
+            // ==Icon Tab==
+            getIcon().setIsChangable(false);
 
             vmAttachedToPool = true;
         }
@@ -1717,6 +1735,7 @@
         getAutoConverge().setItems(Arrays.asList(null, true, false));
         setMigrateCompressed(new NotChangableForVmInPoolListModel<Boolean>());
         getMigrateCompressed().setItems(Arrays.asList(null, true, false));
+        setIcon(new NotChangableForVmInPoolEntityModel<IconWithDefault>());
     }
 
     public void initialize(SystemTreeItemModel SystemTreeSelectedItem)
@@ -2228,7 +2247,29 @@
         updateWatchdogModels(osType);
 
         vmInitEnabledChanged();
-        getInstanceImages().updateActionsAvailability();
+        updateIconAccordingToOs();
+    }
+
+    private void updateIconAccordingToOs() {
+        final Integer osId = getOSType().getSelectedItem();
+        final Guid defaultIconId = 
AsyncDataProvider.getInstance().getDefaultIconId(osId);
+        IconCache.getInstance().getOrFetchIcon(defaultIconId, new 
IconCache.IconCallback() {
+            @Override
+            public void onSuccess(String currentOsDefaultIcon) {
+                String iconToShow = getIconToShow(currentOsDefaultIcon);
+                IconWithDefault newIconPair = new IconWithDefault(iconToShow, 
currentOsDefaultIcon);
+                getIcon().setEntity(newIconPair);
+            }
+        });
+    }
+
+    protected String getIconToShow(String currentOsDefaultIcon) {
+        if (getIcon().getEntity() == null) {
+            return  currentOsDefaultIcon;
+        }
+        return getIcon().getEntity().isCustom()
+                ? getIcon().getEntity().getIcon()
+                : currentOsDefaultIcon;
     }
 
     private void updateWatchdogModels() {
@@ -2714,6 +2755,9 @@
         boolean vmInitIsValid = getVmInitModel().validate();
         setValidTab(TabName.FIRST_RUN, vmInitIsValid);
 
+        getIcon().validateEntity(new IValidation[]{new 
IconWithDefaultValidation()});
+        setValidTab(TabName.ICON_TAB, getIcon().getIsValid());
+
         boolean isValid = hwPartValid && vmInitIsValid && allTabsValid();
         getValid().setEntity(isValid);
         ValidationCompleteEvent.fire(getEventBus(), this);
@@ -2802,6 +2846,7 @@
         setValidTab(TabName.CONSOLE_TAB, true);
         setValidTab(TabName.INITIAL_RUN_TAB, true);
         setValidTab(TabName.GENERAL_TAB, true);
+        setValidTab(TabName.ICON_TAB, true);
         getValid().setEntity(true);
     }
 
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java
index 0a17523..01fd790 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmListModel.java
@@ -61,6 +61,7 @@
 import org.ovirt.engine.ui.uicommonweb.Cloner;
 import 
org.ovirt.engine.ui.uicommonweb.ConsoleOptionsFrontendPersister.ConsoleContext;
 import org.ovirt.engine.ui.uicommonweb.ErrorPopupManager;
+import org.ovirt.engine.ui.uicommonweb.IconCache;
 import org.ovirt.engine.ui.uicommonweb.Linq;
 import org.ovirt.engine.ui.uicommonweb.TagsEqualityComparer;
 import org.ovirt.engine.ui.uicommonweb.TypeResolver;
@@ -1395,15 +1396,15 @@
     }
 
     protected static VM buildVmOnNewTemplate(UnitVmModel model, VM vm) {
-        VM tempVar = new VM();
-        tempVar.setId(vm.getId());
-        BuilderExecutor.build(model, tempVar.getStaticData(), new 
CommonUnitToVmBaseBuilder());
-        BuilderExecutor.build(vm.getStaticData(), tempVar.getStaticData(),
+        VM resultVm = new VM();
+        resultVm.setId(vm.getId());
+        BuilderExecutor.build(model, resultVm.getStaticData(), new 
CommonUnitToVmBaseBuilder());
+        BuilderExecutor.build(vm.getStaticData(), resultVm.getStaticData(),
                               new KernelParamsVmBaseToVmBaseBuilder(),
                               new DedicatedVmForVdsVmBaseToVmBaseBuilder(),
                               new MigrationOptionsVmBaseToVmBaseBuilder(),
                               new UsbPolicyVmBaseToVmBaseBuilder());
-        return tempVar;
+        return resultVm;
     }
 
     private void migrate()
@@ -1865,8 +1866,9 @@
         getcurrentVm().setCreatedByUserId(selectedItem.getCreatedByUserId());
         
getcurrentVm().setUseLatestVersion(model.getTemplateWithVersion().getSelectedItem().isLatest());
 
+        final String iconForCommandParams = getIconIfChanged(model, 
getcurrentVm());
             if (selectedItem.isRunningOrPaused()) {
-                
AsyncDataProvider.getInstance().getVmChangedFieldsForNextRun(editedVm, 
getcurrentVm(), getUpdateVmParameters(false), new AsyncQuery(this,
+                
AsyncDataProvider.getInstance().getVmChangedFieldsForNextRun(editedVm, 
getcurrentVm(), getUpdateVmParameters(false, null), new AsyncQuery(this,
                         new INewAsyncCallback() {
                     @Override
                     public void onSuccess(Object thisModel, Object 
returnValue) {
@@ -1879,6 +1881,7 @@
                             confirmModel.setChangedFields(changedFields);
                             
confirmModel.setCpuPluggable(selectedItem.getCpuPerSocket() == 
getcurrentVm().getCpuPerSocket() &&
                                     selectedItem.getNumOfSockets() != 
getcurrentVm().getNumOfSockets());
+                            confirmModel.setIcon(iconForCommandParams);
 
                             confirmModel.getCommands().add(new 
UICommand("updateExistingVm", VmListModel.this) //$NON-NLS-1$
                             
.setTitle(ConstantsManager.getInstance().getConstants().ok())
@@ -1889,17 +1892,23 @@
                             setConfirmWindow(confirmModel);
                         }
                         else {
-                            updateExistingVm(false);
+                            updateExistingVm(false, iconForCommandParams);
                         }
                     }
                 }));
             }
             else {
-                updateExistingVm(false);
+                updateExistingVm(false, iconForCommandParams);
             }
     }
 
-    private void updateExistingVm(final boolean applyCpuChangesLater) {
+    private String getIconIfChanged(UnitVmModel model, VM vmOriginalState) {
+        final String newIcon = model.getIcon().getEntity().getIcon();
+        final String originalIcon = 
IconCache.getInstance().getIcon(vmOriginalState.getStaticData().getIconId());
+        return newIcon.equals(originalIcon) ? null : newIcon;
+    }
+
+    private void updateExistingVm(final boolean applyCpuChangesLater, final 
String vmIcon) {
         final UnitVmModel model = (UnitVmModel) getWindow();
 
         if (model.getProgress() != null)
@@ -1929,7 +1938,7 @@
                             if (returnValueBase != null && 
returnValueBase.getSucceeded())
                             {
                                 VM vm = vmListModel.getcurrentVm();
-                                VmManagementParametersBase updateVmParams = 
vmListModel.getUpdateVmParameters(applyCpuChangesLater);
+                                VmManagementParametersBase updateVmParams = 
vmListModel.getUpdateVmParameters(applyCpuChangesLater, vmIcon);
                                 
Frontend.getInstance().runAction(VdcActionType.UpdateVm,
                                         updateVmParams, new 
UnitVmModelNetworkAsyncCallback(model, defaultNetworkCreatingManager, 
vm.getId()), vmListModel);
                             }
@@ -1945,12 +1954,12 @@
         else
         {
             model.startProgress(null);
-            VmManagementParametersBase updateVmParams = 
getUpdateVmParameters(applyCpuChangesLater);
+            VmManagementParametersBase updateVmParams = 
getUpdateVmParameters(applyCpuChangesLater, vmIcon);
             Frontend.getInstance().runAction(VdcActionType.UpdateVm, 
updateVmParams, new UnitVmModelNetworkAsyncCallback(model, 
defaultNetworkCreatingManager, getcurrentVm().getId()), this);
         }
     }
 
-    public VmManagementParametersBase getUpdateVmParameters(boolean 
applyCpuChangesLater) {
+    public VmManagementParametersBase getUpdateVmParameters(boolean 
applyCpuChangesLater, String vmIcon) {
         UnitVmModel model = (UnitVmModel) getWindow();
         VmManagementParametersBase updateVmParams = new 
VmManagementParametersBase(getcurrentVm());
 
@@ -1961,6 +1970,7 @@
         
updateVmParams.setVirtioScsiEnabled(model.getIsVirtioScsiEnabled().getEntity());
         updateVmParams.setApplyChangesLater(applyCpuChangesLater);
         updateVmParams.setUpdateNuma(model.isNumaChanged());
+        updateVmParams.setVmIcon(vmIcon);
         setRngDeviceToParams(model, updateVmParams);
         BuilderExecutor.build(model, updateVmParams, new 
UnitToGraphicsDeviceParamsBuilder());
 
@@ -2327,7 +2337,7 @@
         }
         else if ("updateExistingVm".equals(command.getName())) { // $NON-NLS-1$
             VmNextRunConfigurationModel model = (VmNextRunConfigurationModel) 
getConfirmWindow();
-            updateExistingVm(model.getApplyCpuLater().getEntity());
+            updateExistingVm(model.getApplyCpuLater().getEntity(), 
model.getIcon());
             cancelConfirmation();
         }
         else if (CMD_CONFIGURE_VMS_TO_IMPORT.equals(command.getName())) {
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java
index e2b6323..5ff46c5 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmModelBehaviorBase.java
@@ -186,7 +186,8 @@
 
     public abstract void provisioning_SelectedItemChanged();
 
-    public abstract void oSType_SelectedItemChanged();
+    public void oSType_SelectedItemChanged() {
+    }
 
     public abstract void updateMinAllocatedMemory();
 
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java
index 0f6091a..1b9a1e2 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/VmNextRunConfigurationModel.java
@@ -10,6 +10,15 @@
     private EntityModel<Boolean> applyCpuLater;
     private boolean cpuPluggable;
     private List<String> changedFields;
+    private String icon;
+
+    public String getIcon() {
+        return icon;
+    }
+
+    public void setIcon(String icon) {
+        this.icon = icon;
+    }
 
     public VmNextRunConfigurationModel() {
         setApplyCpuLater(new EntityModel<Boolean>(false));
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java
index 5b8fb76..439bb2f 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/models/vms/instancetypes/InstanceTypeModelBehaviorBase.java
@@ -94,11 +94,6 @@
     }
 
     @Override
-    public void oSType_SelectedItemChanged() {
-
-    }
-
-    @Override
     public void updateMinAllocatedMemory() {
     }
 
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconValidation.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconValidation.java
new file mode 100644
index 0000000..036265e
--- /dev/null
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconValidation.java
@@ -0,0 +1,112 @@
+package org.ovirt.engine.ui.uicommonweb.validation;
+
+import com.google.gwt.user.client.ui.Image;
+import org.ovirt.engine.ui.uicommonweb.Linq;
+import org.ovirt.engine.ui.uicompat.ConstantsManager;
+
+import java.util.List;
+
+/**
+ * It validates icons size, dimensions and file format.
+ */
+public class IconValidation implements IValidation {
+
+    @Override
+    public ValidationResult validate(Object iconObject) {
+        if (iconObject instanceof String) {
+            final String iconString = (String) iconObject;
+            return validate(iconString);
+        }
+        throw new IllegalArgumentException(
+                "Illegal argument type: " + iconObject == null ? "null" : 
iconObject.getClass().toString()); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    private ValidationResult validate(String icon) {
+        ValidationResult typeValidation = validateType(icon);
+        if (!typeValidation.getSuccess()) {
+            return typeValidation;
+        }
+        ValidationResult dimensionsValidation = validateDimensions(icon);
+        ValidationResult sizeValidation = validateSize(icon);
+        if (dimensionsValidation.getSuccess() && sizeValidation.getSuccess()) {
+            return ValidationResult.ok();
+        }
+        final List<String> reasons = 
Linq.concatTypesafe(dimensionsValidation.getReasons(), 
sizeValidation.getReasons());
+        return new ValidationResult(false, reasons);
+    }
+
+    /**
+     * Max width 150px, max height 120px
+     */
+    private ValidationResult validateDimensions(String icon) {
+        final int maxWidth = 150;
+        final int maxHeight = 120;
+        final Image image = new Image(icon);
+        boolean valid = image.getWidth() <= maxWidth
+                && image.getHeight() <= maxHeight;
+        if (valid) {
+            return ValidationResult.ok();
+        }
+        return ValidationResult.fail(
+                
ConstantsManager.getInstance().getMessages().iconDimensionsTooLarge(
+                        image.getWidth(), image.getWidth(), maxWidth, 
maxHeight));
+    }
+
+    /**
+     * The dataUri string has to fit in 32kB.
+     * Ratio base64encoded/raw data is approx. 4/3.
+     */
+    private ValidationResult validateSize(String icon) {
+        final int maxEncodedSize = 32 * 1024;
+        final int maxRawSize = new Double(maxEncodedSize * (3d/4d) / 
1024).intValue(); // just estimate for users, in kB
+        boolean valid = maxEncodedSize > icon.length();
+        if (valid) {
+            return ValidationResult.ok();
+        }
+        return ValidationResult.fail(
+                
ConstantsManager.getInstance().getMessages().iconFileTooLarge(maxRawSize));
+    }
+
+    /**
+     * Magic numbers
+     * <pre>
+     *     png 89 50 4e 47 0d 0a 1a 0a
+     *     jpg ff d8 ff
+     *     gif 'GIF87a' or 'GIF89a'
+     * </pre>
+     */
+    private ValidationResult validateType(String icon) {
+        final String iconBase64Data = dataUriToBase64Data(icon);
+        final String iconRawData = atob(iconBase64Data);
+        final String[] magicNumbers = new String[] {
+                "\u0089\u0050\u004e\u0047\r\n\u001a\n", //$NON-NLS-1$ // png
+                "\u00ff\u00d8\u00ff", //$NON-NLS-1$ // png
+                "GIF87a", //$NON-NLS-1$ // gif
+                "GIF89a" //$NON-NLS-1$ // gif
+        };
+        for (String magicNumber : magicNumbers) {
+            if (iconRawData.startsWith(magicNumber)) {
+                return ValidationResult.ok();
+            }
+        }
+        return 
ValidationResult.fail(ConstantsManager.getInstance().getMessages().invalidIconFormat("png,
 jpg, gif")); //$NON-NLS-1$
+    }
+
+    private native String atob(String encodedString) /*-{
+        return atob(encodedString);
+    }-*/;
+
+
+    /**
+     * Datauri format:
+     * <pre>
+     *     data:[&lt;MIME-type>][;charset=<encoding>][;base64],&lt;data>
+     * </pre>
+     * @param dataUri
+     * @return base46 part of datauri
+     */
+    private String dataUriToBase64Data(String dataUri) {
+        final int commaIndex = dataUri.indexOf(","); //$NON-NLS-1$
+        return dataUri.substring(commaIndex + 1);
+    }
+}
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconWithDefaultValidation.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconWithDefaultValidation.java
new file mode 100644
index 0000000..e73b4ed
--- /dev/null
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/IconWithDefaultValidation.java
@@ -0,0 +1,20 @@
+package org.ovirt.engine.ui.uicommonweb.validation;
+
+import org.ovirt.engine.ui.uicommonweb.models.vms.IconWithDefault;
+
+public class IconWithDefaultValidation implements IValidation {
+
+    @Override
+    public ValidationResult validate(Object value) {
+        if (value instanceof IconWithDefault) {
+            final IconWithDefault iconWithDefault = (IconWithDefault) value;
+            return validate(iconWithDefault);
+        }
+        throw new IllegalArgumentException(
+                "Illegal argument type: " + value == null ? "null" : 
value.getClass().toString()); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    private ValidationResult validate(IconWithDefault iconWithDefault) {
+        return new IconValidation().validate(iconWithDefault.getIcon());
+    }
+}
diff --git 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.java
 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.java
index 99ded31..fef57e7 100644
--- 
a/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.java
+++ 
b/frontend/webadmin/modules/uicommonweb/src/main/java/org/ovirt/engine/ui/uicommonweb/validation/ValidationResult.java
@@ -33,7 +33,7 @@
 
     public ValidationResult()
     {
-        this(true, new ArrayList<String>());
+        this(true, new ArrayList<String>(0));
     }
 
     public ValidationResult(boolean success, List<String> reasons) {
@@ -48,4 +48,12 @@
     public static ValidationResult fail(String... reasons) {
         return new ValidationResult(false, Arrays.asList(reasons));
     }
+
+    @Override
+    public String toString() {
+        return "ValidationResult{" + //$NON-NLS-1$
+                "success=" + privateSuccess + //$NON-NLS-1$
+                ", reasons=" + privateReasons + //$NON-NLS-1$
+                "}"; //$NON-NLS-1$
+    }
 }
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 38c2bbe..46bed21 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
@@ -985,7 +985,7 @@
     String diskExistsOnAllActiveStorageDomainsMsg();
 
     @DefaultStringValue("The Template that the VM is based on does not exist 
on any active Storage Domain")
-    String noActiveStorageDomainWithTemplateMsg();;
+    String noActiveStorageDomainWithTemplateMsg();
 
     @DefaultStringValue("Field value should follow: 
<parameter=value;parameter=value;...>")
     String fieldValueShouldFollowMsg();
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 2b6fdf1..4bc51b3 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
@@ -417,7 +417,16 @@
 
     @DefaultMessage("Force {0} session")
     String geoRepForceTitle(String action);
+    
+    @DefaultMessage("Icon dimensions are too large: {0}x{1}, maximum allowed: 
{2}x{3}")
+    String iconDimensionsTooLarge(int width, int height, int maxWidht, int 
maxHeight);
 
+    @DefaultMessage("Icon file is too large. Maximum size is {0}kB.")
+    String iconFileTooLarge(int maxSize);
+
+    @DefaultMessage("Unknown file format. Supported formats are {0}.")
+    String invalidIconFormat(String s);
+    
     @DefaultMessage("Value of cluster configuration parameter {0} is empty")
     String clusterSnapshotOptionValueEmpty(String option);
 


-- 
To view, visit https://gerrit.ovirt.org/38601
To unsubscribe, visit https://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Id56aecccef6aab6604de32e27497991ab3713d8d
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Jakub Niedermertl <jnied...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to