This is an automated email from the ASF dual-hosted git repository.

weizhou pushed a commit to branch 4.18
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.18 by this push:
     new e4117e68d2f engine/schema: fix duplicated guest OSes in 4.18.0.0 
(#7799)
e4117e68d2f is described below

commit e4117e68d2f04e307aba8b3d9b98cc248de384e1
Author: Wei Zhou <[email protected]>
AuthorDate: Wed Aug 23 09:44:34 2023 +0200

    engine/schema: fix duplicated guest OSes in 4.18.0.0 (#7799)
    
    Co-authored-by: Daan Hoogland <[email protected]>
---
 api/src/main/java/com/cloud/storage/GuestOS.java   |   2 +
 .../api/command/admin/guest/AddGuestOsCmd.java     |   8 +-
 .../api/command/admin/guest/UpdateGuestOsCmd.java  |  11 ++-
 .../api/command/user/guest/ListGuestOsCmd.java     |  14 ++-
 .../cloudstack/api/response/GuestOSResponse.java   |  12 +++
 .../src/main/java/com/cloud/storage/GuestOSVO.java |  14 +++
 .../java/com/cloud/storage/dao/GuestOSDao.java     |  13 ++-
 .../java/com/cloud/storage/dao/GuestOSDaoImpl.java |  78 ++++++++++++++-
 .../cloud/storage/dao/GuestOSHypervisorDao.java    |   8 +-
 .../storage/dao/GuestOSHypervisorDaoImpl.java      |   8 +-
 .../main/java/com/cloud/upgrade/GuestOsMapper.java | 110 +++++++++++++++++++--
 .../com/cloud/upgrade/dao/Upgrade41800to41810.java |  12 +++
 .../resources/META-INF/db/schema-41720to41800.sql  |   4 +-
 .../resources/META-INF/db/schema-41800to41810.sql  |   4 +
 .../java/com/cloud/hypervisor/guru/VMwareGuru.java |   4 +-
 server/src/main/java/com/cloud/api/ApiDBUtils.java |   2 +-
 .../main/java/com/cloud/api/ApiResponseHelper.java |   1 +
 .../com/cloud/server/ManagementServerImpl.java     |  49 +++------
 .../cloudstack/vm/UnmanagedVMsManagerImpl.java     |   2 +-
 19 files changed, 295 insertions(+), 61 deletions(-)

diff --git a/api/src/main/java/com/cloud/storage/GuestOS.java 
b/api/src/main/java/com/cloud/storage/GuestOS.java
index 371260bec64..90b904758a9 100644
--- a/api/src/main/java/com/cloud/storage/GuestOS.java
+++ b/api/src/main/java/com/cloud/storage/GuestOS.java
@@ -34,4 +34,6 @@ public interface GuestOS extends InternalIdentity, Identity {
     Date getRemoved();
 
     boolean getIsUserDefined();
+
+    boolean getForDisplay();
 }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java
index 2b95c209bb8..b0d59d49362 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java
@@ -27,6 +27,7 @@ import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
 import org.apache.cloudstack.api.response.GuestOSResponse;
+import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.context.CallContext;
 
 import com.cloud.event.EventTypes;
@@ -60,6 +61,8 @@ public class AddGuestOsCmd extends BaseAsyncCreateCmd {
     @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = 
false, description = "Map of (key/value pairs)")
     private Map details;
 
+    @Parameter(name="forDisplay", type=CommandType.BOOLEAN, 
description="whether this guest OS is available for end users", authorized = 
{RoleType.Admin})
+    private Boolean display;
 
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
@@ -78,7 +81,7 @@ public class AddGuestOsCmd extends BaseAsyncCreateCmd {
     }
 
     public Map getDetails() {
-        Map<String, String> detailsMap = new HashMap<String, String>();
+        Map<String, String> detailsMap = new HashMap<>();
         if (!details.isEmpty()) {
             Collection<?> servicesCollection = details.values();
             Iterator<?> iter = servicesCollection.iterator();
@@ -92,6 +95,9 @@ public class AddGuestOsCmd extends BaseAsyncCreateCmd {
         return detailsMap;
     }
 
+    public Boolean getForDisplay() {
+        return display;
+    }
 
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java
index 1168bf00ffc..502b1f8faae 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java
@@ -26,6 +26,7 @@ import org.apache.cloudstack.api.BaseAsyncCmd;
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ServerApiException;
 import org.apache.cloudstack.api.response.GuestOSResponse;
+import org.apache.cloudstack.acl.RoleType;
 
 import com.cloud.event.EventTypes;
 import com.cloud.storage.GuestOS;
@@ -56,8 +57,10 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd {
     @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = 
true, description = "Map of (key/value pairs)")
     private Map details;
 
+    @Parameter(name="forDisplay", type=CommandType.BOOLEAN, 
description="whether this guest OS is available for end users", authorized = 
{RoleType.Admin})
+    private Boolean display;
 
-/////////////////////////////////////////////////////
+    /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
 
@@ -70,7 +73,7 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd {
     }
 
     public Map getDetails() {
-        Map<String, String> detailsMap = new HashMap<String, String>();;
+        Map<String, String> detailsMap = new HashMap<>();;
         if (!details.isEmpty()) {
             Collection<?> servicesCollection = details.values();
             Iterator<?> iter = servicesCollection.iterator();
@@ -84,6 +87,10 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd {
         return detailsMap;
     }
 
+    public Boolean getForDisplay() {
+        return display;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java
index 7419cd8f759..9d6cd438561 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java
@@ -19,8 +19,10 @@ package org.apache.cloudstack.api.command.user.guest;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.log4j.Logger;
 
+import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseListCmd;
@@ -51,6 +53,10 @@ public class ListGuestOsCmd extends BaseListCmd {
     @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, 
description = "list os by description", since = "3.0.1")
     private String description;
 
+    @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, 
description = "list resources by display flag; only ROOT admin is eligible to 
pass this parameter",
+            since = "4.18.1", authorized = {RoleType.Admin})
+    private Boolean display;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -67,6 +73,10 @@ public class ListGuestOsCmd extends BaseListCmd {
         return description;
     }
 
+    public Boolean getDisplay() {
+        return BooleanUtils.toBooleanDefaultIfNull(display, true);
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
@@ -74,8 +84,8 @@ public class ListGuestOsCmd extends BaseListCmd {
     @Override
     public void execute() {
         Pair<List<? extends GuestOS>, Integer> result = 
_mgr.listGuestOSByCriteria(this);
-        ListResponse<GuestOSResponse> response = new 
ListResponse<GuestOSResponse>();
-        List<GuestOSResponse> osResponses = new ArrayList<GuestOSResponse>();
+        ListResponse<GuestOSResponse> response = new ListResponse<>();
+        List<GuestOSResponse> osResponses = new ArrayList<>();
         for (GuestOS guestOS : result.first()) {
             GuestOSResponse guestOSResponse = 
_responseGenerator.createGuestOSResponse(guestOS);
             osResponses.add(guestOSResponse);
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java 
b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java
index c1a57c3e0e1..7ec2677ca32 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSResponse.java
@@ -43,6 +43,10 @@ public class GuestOSResponse extends BaseResponse {
     @Param(description = "is the guest OS user defined")
     private Boolean isUserDefined;
 
+    @SerializedName(ApiConstants.FOR_DISPLAY)
+    @Param(description = "is the guest OS visible for the users")
+    private Boolean forDisplay;
+
     public String getId() {
         return id;
     }
@@ -75,4 +79,12 @@ public class GuestOSResponse extends BaseResponse {
         this.isUserDefined = isUserDefined;
     }
 
+    public Boolean getForDisplay() {
+        return this.forDisplay;
+    }
+
+    public void setForDisplay(final Boolean forDisplay) {
+        this.forDisplay = forDisplay;
+    }
+
 }
diff --git a/engine/schema/src/main/java/com/cloud/storage/GuestOSVO.java 
b/engine/schema/src/main/java/com/cloud/storage/GuestOSVO.java
index f04f9a4208c..03955f17667 100644
--- a/engine/schema/src/main/java/com/cloud/storage/GuestOSVO.java
+++ b/engine/schema/src/main/java/com/cloud/storage/GuestOSVO.java
@@ -57,6 +57,9 @@ public class GuestOSVO implements GuestOS {
     @Column(name = "is_user_defined")
     private boolean isUserDefined;
 
+    @Column(name = "display", updatable = true, nullable = false)
+    protected boolean display = true;
+
     @Override
     public long getId() {
         return id;
@@ -120,4 +123,15 @@ public class GuestOSVO implements GuestOS {
     public void setIsUserDefined(boolean isUserDefined) {
         this.isUserDefined = isUserDefined;
     }
+
+    public boolean getForDisplay() {
+        return isDisplay();
+    }
+    public boolean isDisplay() {
+        return display;
+    }
+
+    public void setDisplay(boolean display) {
+        this.display = display;
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java 
b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java
index 83e19b17e25..eeae0bdae00 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java
@@ -16,12 +16,23 @@
 // under the License.
 package com.cloud.storage.dao;
 
+import com.cloud.storage.GuestOS;
 import com.cloud.storage.GuestOSVO;
+import com.cloud.utils.Pair;
 import com.cloud.utils.db.GenericDao;
 
+import java.util.List;
+import java.util.Set;
+
 public interface GuestOSDao extends GenericDao<GuestOSVO, Long> {
 
-    GuestOSVO listByDisplayName(String displayName);
+    GuestOSVO findOneByDisplayName(String displayName);
 
     GuestOSVO findByCategoryIdAndDisplayNameOrderByCreatedDesc(long 
categoryId, String displayName);
+
+    Set<String> findDoubleNames();
+
+    List<GuestOSVO> listByDisplayName(String displayName);
+
+    Pair<List<? extends GuestOS>, Integer> listGuestOSByCriteria(Long 
startIndex, Long pageSize, Long id, Long osCategoryId, String description, 
String keyword, Boolean forDisplay);
 }
diff --git 
a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java 
b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java
index 68da2b92acb..19c4e902188 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java
@@ -17,8 +17,19 @@
 package com.cloud.storage.dao;
 
 
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
+import com.cloud.storage.GuestOS;
+import com.cloud.utils.Pair;
+import com.cloud.utils.db.DB;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.commons.collections.CollectionUtils;
 import org.springframework.stereotype.Component;
 
@@ -42,7 +53,7 @@ public class GuestOSDaoImpl extends GenericDaoBase<GuestOSVO, 
Long> implements G
     }
 
     @Override
-    public GuestOSVO listByDisplayName(String displayName) {
+    public GuestOSVO findOneByDisplayName(String displayName) {
         SearchCriteria<GuestOSVO> sc = Search.create();
         sc.setParameters("display_name", displayName);
         return findOneBy(sc);
@@ -62,4 +73,69 @@ public class GuestOSDaoImpl extends 
GenericDaoBase<GuestOSVO, Long> implements G
         }
         return null;
     }
+
+    /**
+     +       "select display_name from"
+     +              "(select display_name, count(1) as count from guest_os go1 
where removed is null group by display_name having count > 1) tab0";
+     *
+     * @return
+     */
+    @Override
+    @DB
+    public Set<String> findDoubleNames() {
+        String selectSql = "SELECT display_name FROM (SELECT display_name, 
count(1) AS count FROM guest_os go1 WHERE removed IS NULL GROUP BY display_name 
HAVING count > 1) tab0";
+        Set<String> names = new HashSet<>();
+        Connection conn = TransactionLegacy.getStandaloneConnection();
+        try {
+            PreparedStatement stmt = conn.prepareStatement(selectSql);
+            ResultSet rs = stmt.executeQuery();
+            while (rs != null && rs.next()) {
+                names.add(rs.getString(1));
+            }
+        } catch (SQLException ex) {
+            throw new CloudRuntimeException("Error while trying to find 
duplicate guest OSses", ex);
+        }
+        return names;
+    }
+
+    /**
+     * get all with a certain display name
+     * @param displayName
+     * @return a list with GuestOS objects
+     */
+    @Override
+    public List<GuestOSVO> listByDisplayName(String displayName) {
+        SearchCriteria<GuestOSVO> sc = Search.create();
+        sc.setParameters("display_name", displayName);
+        return listBy(sc);
+    }
+
+    public Pair<List<? extends GuestOS>, Integer> listGuestOSByCriteria(Long 
startIndex, Long pageSize, Long id, Long osCategoryId, String description, 
String keyword, Boolean forDisplay) {
+        final Filter searchFilter = new Filter(GuestOSVO.class, "displayName", 
true, startIndex, pageSize);
+        final SearchCriteria<GuestOSVO> sc = createSearchCriteria();
+
+        if (id != null) {
+            sc.addAnd("id", SearchCriteria.Op.EQ, id);
+        }
+
+        if (osCategoryId != null) {
+            sc.addAnd("categoryId", SearchCriteria.Op.EQ, osCategoryId);
+        }
+
+        if (description != null) {
+            sc.addAnd("displayName", SearchCriteria.Op.LIKE, "%" + description 
+ "%");
+        }
+
+        if (keyword != null) {
+            sc.addAnd("displayName", SearchCriteria.Op.LIKE, "%" + keyword + 
"%");
+        }
+
+        if (forDisplay != null) {
+            sc.addAnd("display", SearchCriteria.Op.EQ, forDisplay);
+        }
+
+        final Pair<List<GuestOSVO>, Integer> result = searchAndCount(sc, 
searchFilter);
+        return new Pair<>(result.first(), result.second());
+    }
+
 }
diff --git 
a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java 
b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java
index 47a71433a32..f9a91c7d210 100644
--- 
a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java
+++ 
b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDao.java
@@ -16,7 +16,6 @@
 // under the License.
 package com.cloud.storage.dao;
 
-import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.GuestOSHypervisorVO;
 import com.cloud.utils.db.GenericDao;
 
@@ -24,7 +23,12 @@ import java.util.List;
 
 public interface GuestOSHypervisorDao extends GenericDao<GuestOSHypervisorVO, 
Long> {
 
-    HypervisorType findHypervisorTypeByGuestOsId(long guestOsId);
+    /**
+     * list all the mappings for a guesos id
+     * @param guestOsId the guestos to look for
+     * @return a list of mappings
+     */
+    List<GuestOSHypervisorVO> listByGuestOsId(long guestOsId);
 
     GuestOSHypervisorVO findByOsIdAndHypervisor(long guestOsId, String 
hypervisorType, String hypervisorVersion);
 
diff --git 
a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java
 
b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java
index 65f17c29fbc..e03e3f7ce64 100644
--- 
a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java
+++ 
b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java
@@ -20,13 +20,12 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
-import com.cloud.utils.db.QueryBuilder;
 import org.apache.commons.collections.CollectionUtils;
 import org.springframework.stereotype.Component;
 
-import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.GuestOSHypervisorVO;
 import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.QueryBuilder;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
@@ -82,11 +81,10 @@ public class GuestOSHypervisorDaoImpl extends 
GenericDaoBase<GuestOSHypervisorVO
     }
 
     @Override
-    public HypervisorType findHypervisorTypeByGuestOsId(long guestOsId) {
+    public List<GuestOSHypervisorVO> listByGuestOsId(long guestOsId) {
         SearchCriteria<GuestOSHypervisorVO> sc = guestOsSearch.create();
         sc.setParameters("guest_os_id", guestOsId);
-        GuestOSHypervisorVO goh = findOneBy(sc);
-        return HypervisorType.getType(goh.getHypervisorType());
+        return listBy(sc);
     }
 
     @Override
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/GuestOsMapper.java 
b/engine/schema/src/main/java/com/cloud/upgrade/GuestOsMapper.java
index 739eb32261c..20f9d851c3b 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/GuestOsMapper.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/GuestOsMapper.java
@@ -23,7 +23,9 @@ import org.apache.log4j.Logger;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import javax.inject.Inject;
 
@@ -53,6 +55,89 @@ public class GuestOsMapper {
         guestOSDao = new GuestOSDaoImpl();
     }
 
+    public void mergeDuplicates() {
+        LOG.info("merging duplicate guest osses");
+        Set<Set<GuestOSVO>> duplicates = findDuplicates();
+        LOG.debug(String.format("merging %d sets of duplicates", 
duplicates.size()));
+        for (Set<GuestOSVO> setOfGuestOSes : duplicates) {
+            // decide which to (mark as) remove(d)
+            // # highest/lowest id
+            // # or is user_defined == false
+            GuestOSVO guestOSVO = highestIdFrom(setOfGuestOSes);
+            LOG.info(String.format("merging %d duplicates for %s ", 
setOfGuestOSes.size(), guestOSVO.getDisplayName()));
+            makeNormative(guestOSVO, setOfGuestOSes);
+
+        }
+    }
+
+    public void makeNormative(GuestOSVO guestOSVO, Set<GuestOSVO> 
setOfGuestOSes) {
+        for (GuestOSVO oldGuestOs : setOfGuestOSes) {
+            if (guestOSVO.getId() != oldGuestOs.getId()) {
+                List<GuestOSHypervisorVO> mappings = 
guestOSHypervisorDao.listByGuestOsId(oldGuestOs.getId());
+                copyMappings(guestOSVO, mappings);
+                makeHidden(oldGuestOs);
+            }
+        }
+    }
+
+    private void makeHidden(GuestOSVO guestOSVO) {
+        guestOSVO.setDisplay(false);
+        guestOSDao.update(guestOSVO.getId(),guestOSVO);
+    }
+
+    private void copyMappings(GuestOSVO guestOSVO, List<GuestOSHypervisorVO> 
mappings) {
+        for (GuestOSHypervisorVO mapping : mappings) {
+            if (null == 
guestOSHypervisorDao.findByOsIdAndHypervisor(guestOSVO.getId(), 
mapping.getHypervisorType(), mapping.getHypervisorVersion())) {
+                GuestOSHypervisorVO newMap = new GuestOSHypervisorVO();
+                newMap.setGuestOsId(guestOSVO.getId());
+                newMap.setGuestOsName(mapping.getGuestOsName());
+                newMap.setHypervisorType(mapping.getHypervisorType());
+                newMap.setHypervisorVersion(mapping.getHypervisorVersion());
+                guestOSHypervisorDao.persist(newMap);
+            }
+        }
+    }
+
+    private GuestOSVO highestIdFrom(Set<GuestOSVO> setOfGuestOSes) {
+        GuestOSVO rc = null;
+        for (GuestOSVO guestOSVO: setOfGuestOSes) {
+            if (rc == null || (guestOSVO.getId() > rc.getId() && 
!guestOSVO.getIsUserDefined())) {
+                rc = guestOSVO;
+                break;
+            }
+        }
+        return rc;
+    }
+
+    /**
+     *
+     ¨¨¨
+     select * from guest_os go2
+      where display_name
+      in (select display_name from
+                 (select display_name, count(1) as count from guest_os go1 
group by display_name having count > 1) tab0);
+     ¨¨¨
+     * and group them by display_name
+     *
+     *
+     * @return a list of sets of duplicate
+     */
+    private Set<Set<GuestOSVO>> findDuplicates() {
+        Set<Set<GuestOSVO>> rc = new HashSet<>();
+        Set<String> names = guestOSDao.findDoubleNames();
+        for (String name : names) {
+            List<GuestOSVO> guestOsses = guestOSDao.listByDisplayName(name);
+            if (CollectionUtils.isNotEmpty(guestOsses)) {
+                rc.add(new HashSet<>(guestOsses));
+            }
+        }
+        return rc;
+    }
+
+    public List<GuestOSVO> listByDisplayName(String displayName) {
+        return guestOSDao.listByDisplayName(displayName);
+    }
+
     private long getGuestOsId(long categoryId, String displayName) {
         GuestOSVO guestOS = 
guestOSDao.findByCategoryIdAndDisplayNameOrderByCreatedDesc(categoryId, 
displayName);
         long id = 0l;
@@ -76,18 +161,19 @@ public class GuestOsMapper {
     }
 
     public void addGuestOsAndHypervisorMappings(long categoryId, String 
displayName, List<GuestOSHypervisorMapping> mappings) {
-        if (!addGuestOs(categoryId, displayName)) {
-            LOG.warn("Couldn't add the guest OS with category id: " + 
categoryId + " and display name: " + displayName);
-            return;
-        }
-
-        if (CollectionUtils.isEmpty(mappings)) {
-            return;
-        }
-
         long guestOsId = getGuestOsId(categoryId, displayName);
         if (guestOsId == 0) {
             LOG.debug("No guest OS found with category id: " + categoryId + " 
and display name: " + displayName);
+            if (!addGuestOs(categoryId, displayName)) {
+                LOG.warn("Couldn't add the guest OS with category id: " + 
categoryId + " and display name: " + displayName);
+                return;
+            }
+            guestOsId = getGuestOsId(categoryId, displayName);
+        } else {
+            updateToSystemDefined(guestOsId);
+        }
+
+        if (CollectionUtils.isEmpty(mappings)) {
             return;
         }
 
@@ -96,6 +182,12 @@ public class GuestOsMapper {
         }
     }
 
+    private void updateToSystemDefined(long guestOsId) {
+        GuestOSVO guestOsVo = guestOSDao.findById(guestOsId);
+        guestOsVo.setIsUserDefined(false);
+        guestOSDao.update(guestOsId, guestOsVo);// TODO: update 
is_user_defined to false
+    }
+
     public boolean addGuestOs(long categoryId, String displayName) {
         LOG.debug("Adding guest OS with category id: " + categoryId + " and 
display name: " + displayName);
         GuestOSVO guestOS = new GuestOSVO();
diff --git 
a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41800to41810.java 
b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41800to41810.java
index 53bd497ca22..8eb50666ec8 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41800to41810.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41800to41810.java
@@ -19,6 +19,7 @@ package com.cloud.upgrade.dao;
 import com.cloud.hypervisor.Hypervisor;
 import com.cloud.storage.GuestOSHypervisorMapping;
 import com.cloud.upgrade.GuestOsMapper;
+import com.cloud.storage.GuestOSVO;
 import com.cloud.upgrade.SystemVmTemplateRegistration;
 import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.log4j.Logger;
@@ -26,10 +27,13 @@ import org.apache.log4j.Logger;
 import java.io.InputStream;
 import java.sql.Connection;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 
 public class Upgrade41800to41810 implements DbUpgrade, 
DbUpgradeSystemVmTemplate {
     final static Logger LOG = Logger.getLogger(Upgrade41800to41810.class);
+    private GuestOsMapper guestOsMapper = new GuestOsMapper();
+
     private SystemVmTemplateRegistration systemVmTemplateRegistration;
 
     @Override
@@ -64,6 +68,14 @@ public class Upgrade41800to41810 implements DbUpgrade, 
DbUpgradeSystemVmTemplate
         updateGuestOsMappings(conn);
         copyGuestOsMappingsToVMware80u1();
         addForeignKeyToAutoscaleVmprofiles(conn);
+        mergeDuplicateGuestOSes();
+    }
+
+    private void mergeDuplicateGuestOSes() {
+        guestOsMapper.mergeDuplicates();
+        List<GuestOSVO> nines = guestOsMapper.listByDisplayName("Red Hat 
Enterprise Linux 9");
+        GuestOSVO nineDotZero = guestOsMapper.listByDisplayName("Red Hat 
Enterprise Linux 9.0").get(0);
+        guestOsMapper.makeNormative(nineDotZero, new HashSet<>(nines));
     }
 
     @Override
diff --git 
a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql 
b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql
index 2af6723c134..7941424a43f 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql
@@ -1568,4 +1568,6 @@ UPDATE
 SET
   usage_type = 22
 WHERE
-  usage_type = 24 AND usage_display like '% io write';
\ No newline at end of file
+  usage_type = 24 AND usage_display like '% io write';
+
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os', 'display', 'tinyint(1) 
DEFAULT ''1'' COMMENT ''should this guest_os be shown to the end user'' ');
\ No newline at end of file
diff --git 
a/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql 
b/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
index 495c184918c..4ea2bf57292 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41800to41810.sql
@@ -43,3 +43,7 @@ CALL 
`cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.autoscale_vmprofiles', 'user_data_de
 UPDATE `cloud`.`service_offering` so
 SET so.limit_cpu_use = 0
 WHERE so.default_use = 1 AND so.vm_type IN ('domainrouter', 
'secondarystoragevm', 'consoleproxy', 'internalloadbalancervm', 
'elasticloadbalancervm');
+
+-- fix erronous commas in guest_os names
+UPDATE `cloud`.`guest_os_hypervisor` SET guest_os_name = 'rhel9_64Guest' WHERE 
guest_os_name = 'rhel9_64Guest,';
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os', 'display', 'tinyint(1) 
DEFAULT ''1'' COMMENT ''should this guest_os be shown to the end user'' ');
diff --git 
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
 
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
index f148a16473e..db41ab19d56 100644
--- 
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
+++ 
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
@@ -456,8 +456,8 @@ public class VMwareGuru extends HypervisorGuruBase 
implements HypervisorGuru, Co
      */
     private Long getImportingVMGuestOs(VirtualMachineConfigSummary 
configSummary) {
         String guestFullName = configSummary.getGuestFullName();
-        GuestOSVO os = _guestOsDao.listByDisplayName(guestFullName);
-        return os != null ? os.getId() : _guestOsDao.listByDisplayName("Other 
(64-bit)").getId();
+        GuestOSVO os = _guestOsDao.findOneByDisplayName(guestFullName);
+        return os != null ? os.getId() : 
_guestOsDao.findOneByDisplayName("Other (64-bit)").getId();
     }
 
     /**
diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java 
b/server/src/main/java/com/cloud/api/ApiDBUtils.java
index 162b1f38add..c4d7f3cc717 100644
--- a/server/src/main/java/com/cloud/api/ApiDBUtils.java
+++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java
@@ -1104,7 +1104,7 @@ public class ApiDBUtils {
     }
 
     public static GuestOS findGuestOSByDisplayName(String displayName) {
-        return s_guestOSDao.listByDisplayName(displayName);
+        return s_guestOSDao.findOneByDisplayName(displayName);
     }
 
     public static HostVO findHostById(Long hostId) {
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java 
b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index 55f00a609e9..05aee6b6ccb 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -3646,6 +3646,7 @@ public class ApiResponseHelper implements 
ResponseGenerator {
         response.setDescription(guestOS.getDisplayName());
         response.setId(guestOS.getUuid());
         response.setIsUserDefined(guestOS.getIsUserDefined());
+        response.setForDisplay(guestOS.getForDisplay());
         GuestOSCategoryVO category = 
ApiDBUtils.findGuestOsCategoryById(guestOS.getCategoryId());
         if (category != null) {
             response.setOsCategoryId(category.getUuid());
diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java 
b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
index 85e86b5b21b..c6ace852bd3 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -2625,32 +2625,15 @@ public class ManagementServerImpl extends ManagerBase 
implements ManagementServe
 
     @Override
     public Pair<List<? extends GuestOS>, Integer> listGuestOSByCriteria(final 
ListGuestOsCmd cmd) {
-        final Filter searchFilter = new Filter(GuestOSVO.class, "displayName", 
true, cmd.getStartIndex(), cmd.getPageSizeVal());
         final Long id = cmd.getId();
         final Long osCategoryId = cmd.getOsCategoryId();
         final String description = cmd.getDescription();
         final String keyword = cmd.getKeyword();
+        final Long startIndex = cmd.getStartIndex();
+        final Long pageSize = cmd.getPageSizeVal();
+        Boolean forDisplay = cmd.getDisplay();
 
-        final SearchCriteria<GuestOSVO> sc = 
_guestOSDao.createSearchCriteria();
-
-        if (id != null) {
-            sc.addAnd("id", SearchCriteria.Op.EQ, id);
-        }
-
-        if (osCategoryId != null) {
-            sc.addAnd("categoryId", SearchCriteria.Op.EQ, osCategoryId);
-        }
-
-        if (description != null) {
-            sc.addAnd("displayName", SearchCriteria.Op.LIKE, "%" + description 
+ "%");
-        }
-
-        if (keyword != null) {
-            sc.addAnd("displayName", SearchCriteria.Op.LIKE, "%" + keyword + 
"%");
-        }
-
-        final Pair<List<GuestOSVO>, Integer> result = 
_guestOSDao.searchAndCount(sc, searchFilter);
-        return new Pair<List<? extends GuestOS>, Integer>(result.first(), 
result.second());
+        return _guestOSDao.listGuestOSByCriteria(startIndex, pageSize, id, 
osCategoryId, description, keyword, forDisplay);
     }
 
     @Override
@@ -2796,18 +2779,20 @@ public class ManagementServerImpl extends ManagerBase 
implements ManagementServe
         guestOsVo.setDisplayName(displayName);
         guestOsVo.setName(name);
         guestOsVo.setIsUserDefined(true);
+        guestOsVo.setDisplay(cmd.getForDisplay() == null ? true : 
cmd.getForDisplay());
         final GuestOS guestOsPersisted = _guestOSDao.persist(guestOsVo);
 
-        if (cmd.getDetails() != null && !cmd.getDetails().isEmpty()) {
-            Map<String, String> detailsMap = cmd.getDetails();
-            for (Object key : detailsMap.keySet()) {
-                _guestOsDetailsDao.addDetail(guestOsPersisted.getId(), 
(String)key, detailsMap.get(key), false);
-            }
-        }
+        persistGuestOsDetails(cmd.getDetails(), guestOsPersisted.getId());
 
         return guestOsPersisted;
     }
 
+    private void persistGuestOsDetails(Map<String, String> details, long 
guestOsPersistedId) {
+        for (Object key : details.keySet()) {
+            _guestOsDetailsDao.addDetail(guestOsPersistedId, (String)key, 
details.get(key), false);
+        }
+    }
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_ADD, eventDescription = 
"Adding a new guest OS type", async = true)
     public GuestOS getAddedGuestOs(final Long guestOsId) {
@@ -2831,12 +2816,7 @@ public class ManagementServerImpl extends ManagerBase 
implements ManagementServe
             throw new InvalidParameterValueException("Unable to modify system 
defined guest OS");
         }
 
-        if (cmd.getDetails() != null && !cmd.getDetails().isEmpty()) {
-            Map<String, String> detailsMap = cmd.getDetails();
-            for (Object key : detailsMap.keySet()) {
-                _guestOsDetailsDao.addDetail(id, (String)key, 
detailsMap.get(key), false);
-            }
-        }
+        persistGuestOsDetails(cmd.getDetails(), id);
 
         //Check if update is needed
         if (displayName.equals(guestOsHandle.getDisplayName())) {
@@ -2850,6 +2830,9 @@ public class ManagementServerImpl extends ManagerBase 
implements ManagementServe
         }
         final GuestOSVO guestOs = _guestOSDao.createForUpdate(id);
         guestOs.setDisplayName(displayName);
+        if (cmd.getForDisplay() != null) {
+            guestOs.setDisplay(cmd.getForDisplay());
+        }
         if (_guestOSDao.update(id, guestOs)) {
             return _guestOSDao.findById(id);
         } else {
diff --git 
a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java 
b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
index 72ccf621e3a..eab1e98c800 100644
--- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
@@ -1214,7 +1214,7 @@ public class UnmanagedVMsManagerImpl implements 
UnmanagedVMsManager {
                         String osName = unmanagedInstance.getOperatingSystem();
                         GuestOS guestOS = null;
                         if (StringUtils.isNotEmpty(osName)) {
-                            guestOS = guestOSDao.listByDisplayName(osName);
+                            guestOS = guestOSDao.findOneByDisplayName(osName);
                         }
                         GuestOSHypervisor guestOSHypervisor = null;
                         if (guestOS != null) {


Reply via email to