diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetVdsCertificateSubjectsByVmIdsQuery.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetVdsCertificateSubjectsByVmIdsQuery.java
new file mode 100644
index 00000000000..16a317cd12c
--- /dev/null
+++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetVdsCertificateSubjectsByVmIdsQuery.java
@@ -0,0 +1,71 @@
+package org.ovirt.engine.core.bll;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+import org.ovirt.engine.core.bll.context.EngineContext;
+import org.ovirt.engine.core.common.businessentities.VdsStatic;
+import org.ovirt.engine.core.common.businessentities.VmDynamic;
+import org.ovirt.engine.core.common.queries.IdsQueryParameters;
+import org.ovirt.engine.core.common.queries.QueryReturnValue;
+import org.ovirt.engine.core.compat.Guid;
+import org.ovirt.engine.core.dao.VdsStaticDao;
+import org.ovirt.engine.core.dao.VmDynamicDao;
+import org.ovirt.engine.core.utils.CertificateSubjectHelper;
+
+
+public class GetVdsCertificateSubjectsByVmIdsQuery
extends QueriesCommandBase
{
+ @Inject
+ private VmDynamicDao vmDynamicDao;
+
+ @Inject
+ private VdsStaticDao vdsStaticDao;
+
+ public GetVdsCertificateSubjectsByVmIdsQuery(P parameters, EngineContext engineContext) {
+ super(parameters, engineContext);
+ }
+
+ @Override
+ protected void executeQueryCommand() {
+ // Initially we set the command as failed:
+ QueryReturnValue queryReturnValue = getQueryReturnValue();
+ queryReturnValue.setSucceeded(false);
+
+ // Check if the virtual machines are running on hosts, and if so then retrieve the hosts and copy the subject
+ // of the certificate to the value returned by the query:
+ List vms = vmDynamicDao.getByIds(getParameters().getIds());
+ List vdsIds = vms.stream()
+ .map(VmDynamic::getRunOnVds)
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toList());
+ if (!vdsIds.isEmpty()) {
+ List vdss = vdsStaticDao.getByIds(vdsIds);
+ // Collect certificate subjects for all hosts running the VMs
+ Map certificateSubjects = vdss.stream()
+ .collect(Collectors.toMap(
+ VdsStatic::getId,
+ vds -> CertificateSubjectHelper.getCertificateSubject(vds.getHostName())));
+ // Populate the certificate subjects for corresponding VMs
+ Map certificateForVms = new HashMap<>();
+ for (VmDynamic vm : vms) {
+ Guid vdsId = vm.getRunOnVds();
+ if (vdsId != null) {
+ String subject = certificateSubjects.get(vdsId);
+ if (subject != null) {
+ certificateForVms.put(vm.getId(), subject);
+ }
+ }
+ }
+ if (!certificateForVms.isEmpty()) {
+ queryReturnValue.setSucceeded(true);
+ queryReturnValue.setReturnValue(certificateForVms);
+ }
+ }
+ }
+}
diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/QueryType.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/QueryType.java
index 2c429da1386..3cd353c2017 100644
--- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/QueryType.java
+++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/QueryType.java
@@ -168,6 +168,7 @@ public enum QueryType implements Serializable {
// Cluster
GetVdsCertificateSubjectByVmId(QueryAuthType.User),
+ GetVdsCertificateSubjectsByVmIds(QueryAuthType.User),
GetAllClusters(QueryAuthType.User),
GetClusterById(QueryAuthType.User),
GetClusterByName(QueryAuthType.User),
diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VmDynamicDao.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VmDynamicDao.java
index 6cc445f09f0..05af987602f 100644
--- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VmDynamicDao.java
+++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VmDynamicDao.java
@@ -40,6 +40,14 @@ public interface VmDynamicDao extends GenericDao, StatusAwareDa
@Override
VmDynamic get(Guid id);
+ /**
+ * Get all VmDynamic with the given ids
+ * @param vmIds
+ * the list of VM ids
+ * @return list of corresponding dynamics
+ */
+ List getByIds(List vmIds);
+
/**
* Updates the specified dynamic vm.
*
diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VmDynamicDaoImpl.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VmDynamicDaoImpl.java
index 3776c9ab93d..52ba673deb7 100644
--- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VmDynamicDaoImpl.java
+++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/VmDynamicDaoImpl.java
@@ -163,6 +163,14 @@ public List getAllRunningForUserAndActionGroup(Guid userID, ActionGro
getCustomMapSqlParameterSource().addValue("user_id", userID).addValue("action_group_id", actionGroup.getId()));
}
+ @Override
+ public List getByIds(List vmIds) {
+ return getCallsHandler().executeReadList("GetVmDynamicByVmGuids",
+ createEntityRowMapper(),
+ getCustomMapSqlParameterSource()
+ .addValue("vm_guids", createArrayOfUUIDs(vmIds)));
+ }
+
@Override
protected MapSqlParameterSource createIdParameterMapper(Guid id) {
return getCustomMapSqlParameterSource().addValue("vm_guid", id);
diff --git a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmsResource.java b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmsResource.java
index f2b51f25746..82037bdda60 100644
--- a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmsResource.java
+++ b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmsResource.java
@@ -26,6 +26,7 @@
import org.ovirt.engine.api.common.util.DetailHelper;
import org.ovirt.engine.api.model.ActionableResource;
import org.ovirt.engine.api.model.AutoPinningPolicy;
+import org.ovirt.engine.api.model.Certificate;
import org.ovirt.engine.api.model.Configuration;
import org.ovirt.engine.api.model.ConfigurationType;
import org.ovirt.engine.api.model.Disk;
@@ -731,6 +732,9 @@ protected Vms mapCollection(List> vmsGraphicsDevices =
DisplayHelper.getGraphicsDevicesForMultipleEntities(this, vmIds);
+ // optimization of DB access: retrieve Certificates for all VMs at once
+ Map vmsCertificate =
+ DisplayHelper.getDisplayCertificatesForMultipleEntities(this, vmIds);
for (org.ovirt.engine.core.common.businessentities.VM entity : entities) {
Vm vm = map(entity);
@@ -742,7 +746,7 @@ protected Vms mapCollection(List getDisplayCertificatesForMultipleEntities(BackendResource res, List vmIds) {
+ QueryReturnValue result =
+ res.runQuery(QueryType.GetVdsCertificateSubjectsByVmIds,
+ new IdsQueryParameters(vmIds));
+
+ if (result != null && result.getSucceeded() && result.getReturnValue() != null) {
+ Map certificateForVms = result.getReturnValue();
+
+ String certificateContent = null;
+ final QueryReturnValue caCertificateReturnValue =
+ res.runQuery(QueryType.GetCACertificate, new QueryParametersBase());
+ if (caCertificateReturnValue.getSucceeded()) {
+ certificateContent = caCertificateReturnValue.getReturnValue();
+ }
+ String organizationName = CertificateSubjectHelper.getOrganizationName();
+
+ Map certificates = new HashMap<>();
+ for (Map.Entry e : certificateForVms.entrySet()) {
+ Certificate cert = new Certificate();
+ cert.setSubject(e.getValue());
+ cert.setOrganization(organizationName);
+ cert.setContent(certificateContent);
+
+ certificates.put(e.getKey(), cert);
+ }
+ return certificates;
+ }
+ return Collections.emptyMap();
+ }
+
private static Display extractDisplayFromResource(BaseResource res) {
if (res instanceof Vm) {
return ((Vm) res).getDisplay();
diff --git a/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/resource/BackendVmsResourceTest.java b/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/resource/BackendVmsResourceTest.java
index f5199a99dfa..061c79a75f1 100644
--- a/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/resource/BackendVmsResourceTest.java
+++ b/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/resource/BackendVmsResourceTest.java
@@ -26,6 +26,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
+import org.ovirt.engine.api.model.Certificate;
import org.ovirt.engine.api.model.Configuration;
import org.ovirt.engine.api.model.CreationStatus;
import org.ovirt.engine.api.model.Disk;
@@ -88,13 +89,14 @@ public class BackendVmsResourceTest
private static final String DEFAULT_TEMPLATE_ID = Guid.Empty.toString();
public static final String CERTIFICATE = "O=Redhat,CN=X.Y.Z.Q";
private static final String CA_CERT = "dummy-cert";
+ private static final String ORG = "ORG";
public BackendVmsResourceTest() {
super(new BackendVmsResource(), SearchType.VM, "VMs : ");
}
public static Stream> mockConfiguration() {
- return Stream.of(MockConfigDescriptor.of(ConfigValues.OrganizationName, "ORG"),
+ return Stream.of(MockConfigDescriptor.of(ConfigValues.OrganizationName, ORG),
MockConfigDescriptor.of(ConfigValues.PropagateDiskErrors, false)
);
}
@@ -1161,11 +1163,11 @@ public void testList() throws Exception {
UriInfo uriInfo = setUpUriExpectations(null);
setUpGetGraphicsMultipleExpectations(3);
- setUpQueryExpectations("");
- setUpGetCertificateExpectations(1, 0);
+ setUpGetDisplayCertificatesMultipleExpectations();
setUpGetCaRootExpectations();
+ setUpQueryExpectations("");
collection.setUriInfo(uriInfo);
- verifyCollection(getCollection());
+ verifyCollection(getCollection(), false, true);
}
@Test
@@ -1198,7 +1200,7 @@ private void testListAllConsoleAware(boolean allContent) throws Exception {
setUpQueryExpectations("");
collection.setUriInfo(uriInfo);
- verifyCollection(getCollection());
+ verifyCollection(getCollection(), false, allContent);
}
@Test
@@ -1209,7 +1211,7 @@ public void testListAllContentHeader() throws Exception {
when(httpHeaders.getRequestHeader(BackendResource.ALL_CONTENT_HEADER)).thenReturn(populates);
setUpAllContentExpectations();
collection.setUriInfo(uriInfo);
- verifyCollection(getCollection());
+ verifyCollection(getCollection(), false, true);
}
@Test
@@ -1221,7 +1223,7 @@ public void testListAllContentQueryParameter() throws Exception {
when(uriInfo.getQueryParameters()).thenReturn(queries);
setUpAllContentExpectations();
collection.setUriInfo(uriInfo);
- verifyCollection(getCollection(), true);
+ verifyCollection(getCollection(), true, true);
}
private void setUpAllContentExpectations() throws Exception {
@@ -1501,16 +1503,23 @@ protected List getCollection() {
@Override
protected void verifyCollection(List collection) throws Exception {
- verifyCollection(collection, false);
+ verifyCollection(collection, false, false);
}
- private void verifyCollection(List collection, boolean isPopulated) throws Exception {
+ private void verifyCollection(List collection, boolean isPopulated, boolean hasCertificates) throws Exception {
super.verifyCollection(collection);
boolean populated = isPopulated || checkPopulatedHeader();
for (Vm vm : collection) {
assertTrue(populated ? vm.isSetConsole() : !vm.isSetConsole());
+ assertEquals(hasCertificates, vm.getDisplay().isSetCertificate());
+ if (hasCertificates) {
+ Certificate cert = vm.getDisplay().getCertificate();
+ assertEquals(CERTIFICATE, cert.getSubject());
+ assertEquals(CA_CERT, cert.getContent());
+ assertEquals(ORG, cert.getOrganization());
+ }
}
}
@@ -1705,6 +1714,19 @@ protected void setUpGetGraphicsMultipleExpectations(int times) {
vmDevices);
}
+ protected void setUpGetDisplayCertificatesMultipleExpectations() {
+ Map certificates = new HashMap<>();
+ for (Guid guid : GUIDS) {
+ certificates.put(guid, CERTIFICATE);
+ }
+
+ setUpGetEntityExpectations(QueryType.GetVdsCertificateSubjectsByVmIds,
+ QueryParametersBase.class,
+ new String[]{},
+ new Object[]{},
+ certificates);
+ }
+
protected void setUpGetGraphicsExpectations(int times) {
for (int i = 0; i < times; i++) {
setUpGetEntityExpectations(QueryType.GetGraphicsDevices,
diff --git a/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/util/DisplayHelperTest.java b/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/util/DisplayHelperTest.java
new file mode 100644
index 00000000000..5d3c5d74a5c
--- /dev/null
+++ b/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/util/DisplayHelperTest.java
@@ -0,0 +1,93 @@
+package org.ovirt.engine.api.restapi.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.ovirt.engine.api.model.Certificate;
+import org.ovirt.engine.api.model.Vm;
+import org.ovirt.engine.api.restapi.resource.BackendResource;
+import org.ovirt.engine.core.common.config.ConfigValues;
+import org.ovirt.engine.core.common.queries.QueryReturnValue;
+import org.ovirt.engine.core.common.queries.QueryType;
+import org.ovirt.engine.core.compat.Guid;
+import org.ovirt.engine.core.utils.MockConfigDescriptor;
+import org.ovirt.engine.core.utils.MockConfigExtension;
+
+@MockitoSettings(strictness = Strictness.LENIENT)
+@ExtendWith(MockConfigExtension.class)
+public class DisplayHelperTest {
+ private static final String CERTIFICATE = "O=Redhat,CN=X.Y.Z.Q";
+ private static final String CA_CERT = "dummy-cert";
+ private static final String ORG = "ORG";
+ private static final List GUIDS = Arrays.asList(
+ new Guid("11111111-1111-1111-1111-111111111111"),
+ new Guid("22222222-2222-2222-2222-222222222222"));
+
+ public static Stream> mockConfiguration() {
+ return Stream.of(MockConfigDescriptor.of(ConfigValues.OrganizationName, ORG));
+ }
+
+ @Test
+ public void testAddDisplayCertificate() {
+ Vm vm = new Vm();
+ Certificate certificate = new Certificate();
+ DisplayHelper.addDisplayCertificate(vm, certificate);
+
+ assertSame(certificate, vm.getDisplay().getCertificate());
+ }
+
+ @Test
+ public void testGetDisplayCertificatesForMultipleEntitiesNoResult() {
+ BackendResource res = mock(BackendResource.class);
+ QueryReturnValue result = new QueryReturnValue();
+ result.setSucceeded(false);
+ when(res.runQuery(eq(QueryType.GetVdsCertificateSubjectsByVmIds), any())).thenReturn(result);
+
+ Map certificates = DisplayHelper.getDisplayCertificatesForMultipleEntities(res, GUIDS);
+ assertTrue(certificates.isEmpty());
+ }
+
+ @Test
+ public void testGetDisplayCertificatesForMultipleEntities() {
+ BackendResource res = mock(BackendResource.class);
+
+ QueryReturnValue result = new QueryReturnValue();
+ result.setSucceeded(true);
+ Map subjects = GUIDS.stream().collect(Collectors.toMap(Function.identity(), id -> CERTIFICATE));
+ result.setReturnValue(subjects);
+ when(res.runQuery(eq(QueryType.GetVdsCertificateSubjectsByVmIds), any())).thenReturn(result);
+
+ result = new QueryReturnValue();
+ result.setSucceeded(true);
+ result.setReturnValue(CA_CERT);
+ when(res.runQuery(eq(QueryType.GetCACertificate), any())).thenReturn(result);
+
+
+ Map certificates = DisplayHelper.getDisplayCertificatesForMultipleEntities(res, GUIDS);
+ assertEquals(GUIDS.size(), certificates.size());
+ for (Guid guid : GUIDS) {
+ Certificate cert = certificates.get(guid);
+ assertNotNull(cert);
+ assertEquals(CERTIFICATE, cert.getSubject());
+ assertEquals(CA_CERT, cert.getContent());
+ assertEquals(ORG, cert.getOrganization());
+ }
+ }
+}
diff --git a/packaging/dbscripts/vms_sp.sql b/packaging/dbscripts/vms_sp.sql
index 5a1c875dd9a..e7471f38876 100644
--- a/packaging/dbscripts/vms_sp.sql
+++ b/packaging/dbscripts/vms_sp.sql
@@ -638,6 +638,19 @@ BEGIN
END;$FUNCTION$
LANGUAGE plpgsql;
+CREATE OR REPLACE FUNCTION GetVmDynamicByVmGuids (v_vm_guids UUID[])
+RETURNS SETOF vm_dynamic STABLE AS $FUNCTION$
+BEGIN
+ RETURN QUERY
+
+ SELECT vm_dynamic.*
+ FROM vm_dynamic
+ WHERE vm_guid = ANY(v_vm_guids);
+
+ RETURN;
+END;$FUNCTION$
+LANGUAGE plpgsql;
+
DROP TYPE IF EXISTS GetAllHashesFromVmDynamic_rs CASCADE;
CREATE TYPE GetAllHashesFromVmDynamic_rs AS (vm_guid UUID, hash VARCHAR);
CREATE OR REPLACE FUNCTION GetAllHashesFromVmDynamic ()