From 7d6fc6354dfe2c2a62fa5a8a92de3ae1da1ad5a3 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Sun, 13 Mar 2022 23:27:34 -0300 Subject: [PATCH 01/22] Add direct download certificates listing --- .../apache/cloudstack/api/ApiConstants.java | 1 + .../cloudstack/api/ResponseGenerator.java | 4 + ...stTemplateDirectDownloadCertificasCmd.java | 98 +++++++++++++++++++ .../DirectDownloadCertificateResponse.java | 88 +++++++++++++++++ .../download/DirectDownloadManager.java | 5 + .../download/DirectDownloadCertificateVO.java | 1 + framework/direct-download/pom.xml | 7 ++ .../download/DirectDownloadCertificate.java | 3 +- .../java/com/cloud/api/ApiResponseHelper.java | 17 ++++ .../download/DirectDownloadManagerImpl.java | 19 ++++ 10 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java rename {api/src/main/java/org/apache/cloudstack => framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent}/direct/download/DirectDownloadCertificate.java (92%) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index f2379834a412..b76da1f16d3d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -25,6 +25,7 @@ public class ApiConstants { public static final String ADAPTER_TYPE = "adaptertype"; public static final String ADDRESS = "address"; public static final String ALGORITHM = "algorithm"; + public static final String ALIAS = "alias"; public static final String ALLOCATED_ONLY = "allocatedonly"; public static final String ANNOTATION = "annotation"; public static final String API_KEY = "apikey"; diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 03f0a3c8369f..59e9fdfee9a0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -23,10 +23,12 @@ import java.util.Set; import com.cloud.server.ResourceIcon; +import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import com.cloud.resource.RollingMaintenanceManager; import org.apache.cloudstack.api.response.RollingMaintenanceResponse; +import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -491,4 +493,6 @@ List createTemplateResponses(ResponseView view, VirtualMachine ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon); + DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate); + } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java new file mode 100644 index 000000000000..f195bcf0a3bf --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.direct.download; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; +import org.apache.cloudstack.direct.download.DirectDownloadManager; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = ListTemplateDirectDownloadCertificasCmd.APINAME, + description = "List the uploaded direct download certificates", + responseObject = DirectDownloadCertificateResponse.class, + since = "4.17.0", + authorized = {RoleType.Admin}) +public class ListTemplateDirectDownloadCertificasCmd extends BaseListCmd { + + @Inject + DirectDownloadManager directDownloadManager; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class, + description = "list direct download certificate by ID") + private Long id; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone where certificates are uploaded") + private Long zoneId; + + private static final Logger LOG = Logger.getLogger(ListTemplateDirectDownloadCertificasCmd.class); + public static final String APINAME = "listTemplateDirectDownloadCertificates"; + + private void createResponse(final List certificates) { + final ListResponse response = new ListResponse<>(); + final List responses = new ArrayList<>(); + for (final DirectDownloadCertificate certificate : certificates) { + if (certificate == null) { + continue; + } + DirectDownloadCertificateResponse certificateResponse = _responseGenerator.createDirectDownloadCertificateResponse(certificate); + responses.add(certificateResponse); + } + response.setResponses(responses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + if (id == null && zoneId == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Please specify a direct download id or a zone id for listing certificates"); + } + List certificates = directDownloadManager.listDirectDownloadCertificates(id, zoneId); + createResponse(certificates); + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java new file mode 100644 index 000000000000..81e58e6c2186 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java @@ -0,0 +1,88 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; + +@EntityReference(value = DirectDownloadCertificate.class) +public class DirectDownloadCertificateResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the direct download certificate id") + private String id; + + @SerializedName(ApiConstants.ALIAS) + @Param(description = "the direct download certificate alias") + private String alias; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "the zone id where the certificate is uploaded") + private String zoneId; + + @SerializedName(ApiConstants.CERTIFICATE) + @Param(description = "the direct download certifica") + private String certificate; + + @SerializedName("hypervisor") + @Param(description = "the hypervisor of the hosts where the certificate is uploaded") + private String hypervisor; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public String getCertificate() { + return certificate; + } + + public void setCertificate(String certificate) { + this.certificate = certificate; + } + + public String getHypervisor() { + return hypervisor; + } + + public void setHypervisor(String hypervisor) { + this.hypervisor = hypervisor; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java index f7dfae132a89..049e4d1d6011 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java @@ -17,12 +17,15 @@ package org.apache.cloudstack.direct.download; +import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadService; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import com.cloud.utils.component.PluggableService; +import java.util.List; + public interface DirectDownloadManager extends DirectDownloadService, PluggableService, Configurable { static final int DEFAULT_DIRECT_DOWNLOAD_CONNECT_TIMEOUT = 5000; @@ -59,4 +62,6 @@ public interface DirectDownloadManager extends DirectDownloadService, PluggableS * Revoke direct download certificate with alias 'alias' from hosts of hypervisor type 'hypervisor' */ boolean revokeCertificateAlias(String certificateAlias, String hypervisor, Long zoneId, Long hostId); + + List listDirectDownloadCertificates(Long certificateId, Long zoneId); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java index 0b147d7a2273..166a0a22e293 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.direct.download; import com.cloud.hypervisor.Hypervisor; +import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; import javax.persistence.Column; import javax.persistence.Entity; diff --git a/framework/direct-download/pom.xml b/framework/direct-download/pom.xml index 03a37532e5b5..39acebff54d1 100644 --- a/framework/direct-download/pom.xml +++ b/framework/direct-download/pom.xml @@ -27,4 +27,11 @@ 4.17.0.0-SNAPSHOT ../pom.xml + + + org.apache.cloudstack + cloud-engine-schema + ${project.version} + + \ No newline at end of file diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificate.java b/framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadCertificate.java similarity index 92% rename from api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificate.java rename to framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadCertificate.java index 6227c26ceab4..4719cdf79da9 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificate.java +++ b/framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadCertificate.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.direct.download; +package org.apache.cloudstack.framework.agent.direct.download; import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.api.Identity; @@ -25,5 +25,6 @@ public interface DirectDownloadCertificate extends InternalIdentity, Identity { String getCertificate(); String getAlias(); Hypervisor.HypervisorType getHypervisorType(); + Long getZoneId(); } \ No newline at end of file diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 5df3c17fbd18..743e7a3093bc 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -67,6 +67,7 @@ import org.apache.cloudstack.api.response.CounterResponse; import org.apache.cloudstack.api.response.CreateCmdResponse; import org.apache.cloudstack.api.response.CreateSSHKeyPairResponse; +import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.DomainRouterResponse; @@ -165,6 +166,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.management.ManagementServerHost; @@ -4546,4 +4548,19 @@ public RollingMaintenanceResponse createRollingMaintenanceResponse(Boolean succe public ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon) { return ApiDBUtils.newResourceIconResponse(resourceIcon); } + + @Override + public DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate) { + DirectDownloadCertificateResponse response = new DirectDownloadCertificateResponse(); + DataCenterVO datacenter = ApiDBUtils.findZoneById(certificate.getZoneId()); + if (datacenter != null) { + response.setZoneId(datacenter.getUuid()); + } + response.setId(certificate.getUuid()); + response.setAlias(certificate.getAlias()); + response.setCertificate(certificate.getCertificate()); + response.setHypervisor(certificate.getHypervisorType().name()); + response.setObjectName("directdownloadcertificate"); + return response; + } } diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index 8efe8654026e..43f4362822eb 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; @@ -39,6 +40,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.exception.InvalidParameterValueException; import org.apache.cloudstack.agent.directdownload.DirectDownloadAnswer; import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand; import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand.DownloadProtocol; @@ -56,6 +58,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; +import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; @@ -602,6 +605,22 @@ public boolean revokeCertificateAlias(String certificateAlias, String hypervisor return true; } + @Override + public List listDirectDownloadCertificates(Long certificateId, Long zoneId) { + if (zoneId != null && dataCenterDao.findById(zoneId) == null) { + throw new InvalidParameterValueException(zoneId == null ? "Please enter a zone ID" : "Cannot find a zone with ID = " + zoneId); + } + List certificates = new LinkedList<>(); + if (certificateId != null) { + DirectDownloadCertificateVO certificate = directDownloadCertificateDao.findById(certificateId); + certificates.add(certificate); + } else { + List zoneCertificates = directDownloadCertificateDao.listByZone(zoneId); + certificates.addAll(zoneCertificates); + } + return certificates; + } + protected boolean revokeCertificateAliasFromHost(String alias, Long hostId) { RevokeDirectDownloadCertificateCommand cmd = new RevokeDirectDownloadCertificateCommand(alias); try { From f29fc4dc2d38d4a1375319447f2623ae5116011c Mon Sep 17 00:00:00 2001 From: nvazquez Date: Sun, 13 Mar 2022 23:35:50 -0300 Subject: [PATCH 02/22] Restore class to original project --- .../java/org/apache/cloudstack/api/ResponseGenerator.java | 2 +- .../download/ListTemplateDirectDownloadCertificasCmd.java | 2 +- .../api/response/DirectDownloadCertificateResponse.java | 2 +- .../direct/download/DirectDownloadCertificate.java | 2 +- .../cloudstack/direct/download/DirectDownloadManager.java | 1 - .../direct/download/DirectDownloadCertificateVO.java | 1 - framework/direct-download/pom.xml | 7 ------- server/src/main/java/com/cloud/api/ApiResponseHelper.java | 2 +- .../direct/download/DirectDownloadManagerImpl.java | 1 - 9 files changed, 5 insertions(+), 15 deletions(-) rename {framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent => api/src/main/java/org/apache/cloudstack}/direct/download/DirectDownloadCertificate.java (94%) diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 59e9fdfee9a0..057551dd8194 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -28,7 +28,7 @@ import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import com.cloud.resource.RollingMaintenanceManager; import org.apache.cloudstack.api.response.RollingMaintenanceResponse; -import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; +import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java index f195bcf0a3bf..8ab20eb08759 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java @@ -33,7 +33,7 @@ import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; +import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadManager; import org.apache.log4j.Logger; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java index 81e58e6c2186..44223017dcd0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java @@ -21,7 +21,7 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; -import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; +import org.apache.cloudstack.direct.download.DirectDownloadCertificate; @EntityReference(value = DirectDownloadCertificate.class) public class DirectDownloadCertificateResponse extends BaseResponse { diff --git a/framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadCertificate.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificate.java similarity index 94% rename from framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadCertificate.java rename to api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificate.java index 4719cdf79da9..9c70d84f76d3 100644 --- a/framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadCertificate.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificate.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.framework.agent.direct.download; +package org.apache.cloudstack.direct.download; import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.api.Identity; diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java index 049e4d1d6011..82c93115abab 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.direct.download; -import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadService; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java index 166a0a22e293..0b147d7a2273 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.direct.download; import com.cloud.hypervisor.Hypervisor; -import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; import javax.persistence.Column; import javax.persistence.Entity; diff --git a/framework/direct-download/pom.xml b/framework/direct-download/pom.xml index 39acebff54d1..03a37532e5b5 100644 --- a/framework/direct-download/pom.xml +++ b/framework/direct-download/pom.xml @@ -27,11 +27,4 @@ 4.17.0.0-SNAPSHOT ../pom.xml - - - org.apache.cloudstack - cloud-engine-schema - ${project.version} - - \ No newline at end of file diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 743e7a3093bc..7d8fa9c849b0 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -166,7 +166,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; -import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; +import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.management.ManagementServerHost; diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index 43f4362822eb..4de5acdaeec7 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -58,7 +58,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; -import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; From 0daae7311eb191b8ec5f3b2aa232e2fbcee532ee Mon Sep 17 00:00:00 2001 From: nvazquez Date: Sun, 13 Mar 2022 23:44:19 -0300 Subject: [PATCH 03/22] Small refactor --- .../api/response/DirectDownloadCertificateResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java index 44223017dcd0..e5673c1dfc64 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java @@ -42,7 +42,7 @@ public class DirectDownloadCertificateResponse extends BaseResponse { @Param(description = "the direct download certifica") private String certificate; - @SerializedName("hypervisor") + @SerializedName(ApiConstants.HYPERVISOR) @Param(description = "the hypervisor of the hosts where the certificate is uploaded") private String hypervisor; From 2c1b781ba8058ef794d66dabc26f9cc686505c14 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Mon, 14 Mar 2022 07:44:01 -0300 Subject: [PATCH 04/22] Register API --- server/src/main/java/com/cloud/server/ManagementServerImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index c707b1cb5754..f4d6994e8cdc 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -74,6 +74,7 @@ import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd; +import org.apache.cloudstack.api.command.admin.direct.download.ListTemplateDirectDownloadCertificasCmd; import org.apache.cloudstack.api.command.admin.direct.download.RevokeTemplateDirectDownloadCertificateCmd; import org.apache.cloudstack.api.command.admin.direct.download.UploadTemplateDirectDownloadCertificateCmd; import org.apache.cloudstack.api.command.admin.domain.CreateDomainCmd; @@ -3532,6 +3533,7 @@ public List> getCommands() { cmdList.add(DeleteManagementNetworkIpRangeCmd.class); cmdList.add(UploadTemplateDirectDownloadCertificateCmd.class); cmdList.add(RevokeTemplateDirectDownloadCertificateCmd.class); + cmdList.add(ListTemplateDirectDownloadCertificasCmd.class); cmdList.add(ListMgmtsCmd.class); cmdList.add(GetUploadParamsForIsoCmd.class); cmdList.add(GetRouterHealthCheckResultsCmd.class); From 2904a0e74bf413cd95c68cc3de90e12a51d8eb91 Mon Sep 17 00:00:00 2001 From: Nicolas Vazquez Date: Mon, 14 Mar 2022 08:38:10 -0300 Subject: [PATCH 05/22] Apply suggestions from code review Co-authored-by: Suresh Kumar Anaparti --- .../download/ListTemplateDirectDownloadCertificasCmd.java | 4 ++-- .../api/response/DirectDownloadCertificateResponse.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java index 8ab20eb08759..c99ea7e99d02 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java @@ -46,7 +46,7 @@ responseObject = DirectDownloadCertificateResponse.class, since = "4.17.0", authorized = {RoleType.Admin}) -public class ListTemplateDirectDownloadCertificasCmd extends BaseListCmd { +public class ListTemplateDirectDownloadCertificatesCmd extends BaseListCmd { @Inject DirectDownloadManager directDownloadManager; @@ -80,7 +80,7 @@ private void createResponse(final List certificates) @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { if (id == null && zoneId == null) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Please specify a direct download id or a zone id for listing certificates"); + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Please specify a direct download certificate id or a zone id for listing certificates"); } List certificates = directDownloadManager.listDirectDownloadCertificates(id, zoneId); createResponse(certificates); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java index e5673c1dfc64..05ca88c5ec05 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java @@ -39,7 +39,7 @@ public class DirectDownloadCertificateResponse extends BaseResponse { private String zoneId; @SerializedName(ApiConstants.CERTIFICATE) - @Param(description = "the direct download certifica") + @Param(description = "the direct download certificate") private String certificate; @SerializedName(ApiConstants.HYPERVISOR) From 978fa68523ddf05d957c46182b5980f4f8b81c92 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Mon, 14 Mar 2022 08:42:36 -0300 Subject: [PATCH 06/22] Refactor after review --- ... => ListTemplateDirectDownloadCertificatesCmd.java} | 10 ++++------ .../java/com/cloud/server/ManagementServerImpl.java | 4 ++-- .../direct/download/DirectDownloadManagerImpl.java | 10 +++++----- 3 files changed, 11 insertions(+), 13 deletions(-) rename api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/{ListTemplateDirectDownloadCertificasCmd.java => ListTemplateDirectDownloadCertificatesCmd.java} (90%) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java similarity index 90% rename from api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java rename to api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java index c99ea7e99d02..bad342aad054 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificasCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java @@ -41,7 +41,7 @@ import java.util.ArrayList; import java.util.List; -@APICommand(name = ListTemplateDirectDownloadCertificasCmd.APINAME, +@APICommand(name = ListTemplateDirectDownloadCertificatesCmd.APINAME, description = "List the uploaded direct download certificates", responseObject = DirectDownloadCertificateResponse.class, since = "4.17.0", @@ -59,7 +59,7 @@ public class ListTemplateDirectDownloadCertificatesCmd extends BaseListCmd { description = "the zone where certificates are uploaded") private Long zoneId; - private static final Logger LOG = Logger.getLogger(ListTemplateDirectDownloadCertificasCmd.class); + private static final Logger LOG = Logger.getLogger(ListTemplateDirectDownloadCertificatesCmd.class); public static final String APINAME = "listTemplateDirectDownloadCertificates"; private void createResponse(final List certificates) { @@ -78,10 +78,8 @@ private void createResponse(final List certificates) } @Override - public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { - if (id == null && zoneId == null) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Please specify a direct download certificate id or a zone id for listing certificates"); - } + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { List certificates = directDownloadManager.listDirectDownloadCertificates(id, zoneId); createResponse(certificates); } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index f4d6994e8cdc..de33afae4400 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -74,7 +74,7 @@ import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd; -import org.apache.cloudstack.api.command.admin.direct.download.ListTemplateDirectDownloadCertificasCmd; +import org.apache.cloudstack.api.command.admin.direct.download.ListTemplateDirectDownloadCertificatesCmd; import org.apache.cloudstack.api.command.admin.direct.download.RevokeTemplateDirectDownloadCertificateCmd; import org.apache.cloudstack.api.command.admin.direct.download.UploadTemplateDirectDownloadCertificateCmd; import org.apache.cloudstack.api.command.admin.domain.CreateDomainCmd; @@ -3533,7 +3533,7 @@ public List> getCommands() { cmdList.add(DeleteManagementNetworkIpRangeCmd.class); cmdList.add(UploadTemplateDirectDownloadCertificateCmd.class); cmdList.add(RevokeTemplateDirectDownloadCertificateCmd.class); - cmdList.add(ListTemplateDirectDownloadCertificasCmd.class); + cmdList.add(ListTemplateDirectDownloadCertificatesCmd.class); cmdList.add(ListMgmtsCmd.class); cmdList.add(GetUploadParamsForIsoCmd.class); cmdList.add(GetRouterHealthCheckResultsCmd.class); diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index 4de5acdaeec7..a6dcc5a61c63 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -607,15 +607,15 @@ public boolean revokeCertificateAlias(String certificateAlias, String hypervisor @Override public List listDirectDownloadCertificates(Long certificateId, Long zoneId) { if (zoneId != null && dataCenterDao.findById(zoneId) == null) { - throw new InvalidParameterValueException(zoneId == null ? "Please enter a zone ID" : "Cannot find a zone with ID = " + zoneId); + throw new InvalidParameterValueException("Cannot find a zone with ID = " + zoneId); } List certificates = new LinkedList<>(); if (certificateId != null) { - DirectDownloadCertificateVO certificate = directDownloadCertificateDao.findById(certificateId); - certificates.add(certificate); + certificates.add(directDownloadCertificateDao.findById(certificateId)); + } else if (zoneId != null) { + certificates.addAll(directDownloadCertificateDao.listByZone(zoneId)); } else { - List zoneCertificates = directDownloadCertificateDao.listByZone(zoneId); - certificates.addAll(zoneCertificates); + certificates.addAll(directDownloadCertificateDao.listAll()); } return certificates; } From e969c2d84e7e77cf60ea1b4051735672f2a089c4 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Mon, 14 Mar 2022 09:43:43 -0300 Subject: [PATCH 07/22] Fix checkstyle --- .../download/ListTemplateDirectDownloadCertificatesCmd.java | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java index bad342aad054..e7b46793acc4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java @@ -24,7 +24,6 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; From 596ef37342a37a3075ba2381d01d0fe7feb91299 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Tue, 15 Mar 2022 11:27:54 -0300 Subject: [PATCH 08/22] Add hosts mapping to API response --- .../apache/cloudstack/api/ApiConstants.java | 3 + .../cloudstack/api/ResponseGenerator.java | 3 + ...TemplateDirectDownloadCertificatesCmd.java | 15 +++++ ...ectDownloadCertificateHostMapResponse.java | 61 +++++++++++++++++++ .../DirectDownloadCertificateResponse.java | 26 ++++++++ .../DirectDownloadCertificateHostMap.java | 26 ++++++++ .../download/DirectDownloadManager.java | 14 +++-- .../DirectDownloadCertificateHostMapVO.java | 16 ++--- .../java/com/cloud/api/ApiResponseHelper.java | 23 +++++++ .../download/DirectDownloadManagerImpl.java | 8 +++ 10 files changed, 181 insertions(+), 14 deletions(-) create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostMapResponse.java create mode 100644 api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMap.java diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index b76da1f16d3d..00e893322b28 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -188,6 +188,7 @@ public class ApiConstants { public static final String HOST_ID = "hostid"; public static final String HOST_IDS = "hostids"; public static final String HOST_NAME = "hostname"; + public static final String HOSTS_MAP = "hostsmap"; public static final String HYPERVISOR = "hypervisor"; public static final String INLINE = "inline"; public static final String INSTANCE = "instance"; @@ -237,6 +238,7 @@ public class ApiConstants { public static final String LEVEL = "level"; public static final String LENGTH = "length"; public static final String LIMIT_CPU_USE = "limitcpuuse"; + public static final String LIST_HOSTS = "listhosts"; public static final String LOCK = "lock"; public static final String LUN = "lun"; public static final String LBID = "lbruleid"; @@ -322,6 +324,7 @@ public class ApiConstants { public static final String RESOURCE_TYPE_NAME = "resourcetypename"; public static final String RESPONSE = "response"; public static final String REVERTABLE = "revertable"; + public static final String REVOKED = "revoked"; public static final String REGISTERED = "registered"; public static final String QUALIFIERS = "qualifiers"; public static final String QUERY_FILTER = "queryfilter"; diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 057551dd8194..34d5397860b7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -23,12 +23,14 @@ import java.util.Set; import com.cloud.server.ResourceIcon; +import org.apache.cloudstack.api.response.DirectDownloadCertificateHostMapResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import com.cloud.resource.RollingMaintenanceManager; import org.apache.cloudstack.api.response.RollingMaintenanceResponse; import org.apache.cloudstack.direct.download.DirectDownloadCertificate; +import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -495,4 +497,5 @@ List createTemplateResponses(ResponseView view, VirtualMachine DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate); + List createDirectDownloadCertificateHostMapResponse(List hostMappings); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java index e7b46793acc4..22e5d896a310 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java @@ -28,11 +28,13 @@ import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DirectDownloadCertificateHostMapResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.direct.download.DirectDownloadCertificate; +import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.direct.download.DirectDownloadManager; import org.apache.log4j.Logger; @@ -58,9 +60,17 @@ public class ListTemplateDirectDownloadCertificatesCmd extends BaseListCmd { description = "the zone where certificates are uploaded") private Long zoneId; + @Parameter(name = ApiConstants.LIST_HOSTS, type = CommandType.BOOLEAN, + description = "if set to true: include the hosts where the certificate is uploaded to") + private Boolean listHosts; + private static final Logger LOG = Logger.getLogger(ListTemplateDirectDownloadCertificatesCmd.class); public static final String APINAME = "listTemplateDirectDownloadCertificates"; + public boolean isListHosts() { + return listHosts != null && listHosts; + } + private void createResponse(final List certificates) { final ListResponse response = new ListResponse<>(); final List responses = new ArrayList<>(); @@ -69,6 +79,11 @@ private void createResponse(final List certificates) continue; } DirectDownloadCertificateResponse certificateResponse = _responseGenerator.createDirectDownloadCertificateResponse(certificate); + if (isListHosts()) { + List hostMappings = directDownloadManager.getCertificateHostsMapping(certificate.getId()); + List hostMapResponses = _responseGenerator.createDirectDownloadCertificateHostMapResponse(hostMappings); + certificateResponse.setHostsMap(hostMapResponses); + } responses.add(certificateResponse); } response.setResponses(responses); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostMapResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostMapResponse.java new file mode 100644 index 000000000000..fbeed26dfe33 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostMapResponse.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +public class DirectDownloadCertificateHostMapResponse extends BaseResponse { + + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "the ID of the host") + private String hostId; + + @SerializedName(ApiConstants.HOST_NAME) + @Param(description = "the name of the host") + private String hostName; + + @SerializedName(ApiConstants.REVOKED) + @Param(description = "indicates if the certificate has been revoked from the host") + private Boolean revoked; + + public String getHostId() { + return hostId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public Boolean getRevoked() { + return revoked; + } + + public void setRevoked(Boolean revoked) { + this.revoked = revoked; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java index 05ca88c5ec05..7e3830636d5d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java @@ -23,6 +23,8 @@ import org.apache.cloudstack.api.EntityReference; import org.apache.cloudstack.direct.download.DirectDownloadCertificate; +import java.util.List; + @EntityReference(value = DirectDownloadCertificate.class) public class DirectDownloadCertificateResponse extends BaseResponse { @@ -38,6 +40,10 @@ public class DirectDownloadCertificateResponse extends BaseResponse { @Param(description = "the zone id where the certificate is uploaded") private String zoneId; + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "the zone name where the certificate is uploaded") + private String zoneName; + @SerializedName(ApiConstants.CERTIFICATE) @Param(description = "the direct download certificate") private String certificate; @@ -46,6 +52,10 @@ public class DirectDownloadCertificateResponse extends BaseResponse { @Param(description = "the hypervisor of the hosts where the certificate is uploaded") private String hypervisor; + @SerializedName(ApiConstants.HOSTS_MAP) + @Param(description = "the hosts where the certificate is uploaded to", responseObject = HostResponse.class) + private List hostsMap; + public String getId() { return id; } @@ -85,4 +95,20 @@ public String getHypervisor() { public void setHypervisor(String hypervisor) { this.hypervisor = hypervisor; } + + public String getZoneName() { + return zoneName; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public List getHostsMap() { + return hostsMap; + } + + public void setHostsMap(List hosts) { + this.hostsMap = hosts; + } } diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMap.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMap.java new file mode 100644 index 000000000000..d48eb5564c97 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMap.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.direct.download; + +import org.apache.cloudstack.api.InternalIdentity; + +public interface DirectDownloadCertificateHostMap extends InternalIdentity { + + long getCertificateId(); + long getHostId(); + boolean isRevoked(); +} diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java index 82c93115abab..b00058136bd9 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java @@ -27,9 +27,9 @@ public interface DirectDownloadManager extends DirectDownloadService, PluggableService, Configurable { - static final int DEFAULT_DIRECT_DOWNLOAD_CONNECT_TIMEOUT = 5000; - static final int DEFAULT_DIRECT_DOWNLOAD_SOCKET_TIMEOUT = 5000; - static final int DEFAULT_DIRECT_DOWNLOAD_CONNECTION_REQUEST_TIMEOUT = 5000; + int DEFAULT_DIRECT_DOWNLOAD_CONNECT_TIMEOUT = 5000; + int DEFAULT_DIRECT_DOWNLOAD_SOCKET_TIMEOUT = 5000; + int DEFAULT_DIRECT_DOWNLOAD_CONNECTION_REQUEST_TIMEOUT = 5000; ConfigKey DirectDownloadCertificateUploadInterval = new ConfigKey<>("Advanced", Long.class, "direct.download.certificate.background.task.interval", @@ -39,19 +39,19 @@ public interface DirectDownloadManager extends DirectDownloadService, PluggableS "Only certificates which have not been revoked from hosts are uploaded", false); - static final ConfigKey DirectDownloadConnectTimeout = new ConfigKey("Advanced", Integer.class, + ConfigKey DirectDownloadConnectTimeout = new ConfigKey("Advanced", Integer.class, "direct.download.connect.timeout", String.valueOf(DEFAULT_DIRECT_DOWNLOAD_CONNECT_TIMEOUT), "Connection establishment timeout in milliseconds for direct download", true); - static final ConfigKey DirectDownloadSocketTimeout = new ConfigKey("Advanced", Integer.class, + ConfigKey DirectDownloadSocketTimeout = new ConfigKey("Advanced", Integer.class, "direct.download.socket.timeout", String.valueOf(DEFAULT_DIRECT_DOWNLOAD_SOCKET_TIMEOUT), "Socket timeout (SO_TIMEOUT) in milliseconds for direct download", true); - static final ConfigKey DirectDownloadConnectionRequestTimeout = new ConfigKey("Hidden", Integer.class, + ConfigKey DirectDownloadConnectionRequestTimeout = new ConfigKey("Hidden", Integer.class, "direct.download.connection.request.timeout", String.valueOf(DEFAULT_DIRECT_DOWNLOAD_CONNECTION_REQUEST_TIMEOUT), "Requesting a connection from connection manager timeout in milliseconds for direct download", @@ -63,4 +63,6 @@ public interface DirectDownloadManager extends DirectDownloadService, PluggableS boolean revokeCertificateAlias(String certificateAlias, String hypervisor, Long zoneId, Long hostId); List listDirectDownloadCertificates(Long certificateId, Long zoneId); + + List getCertificateHostsMapping(Long certificateId); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapVO.java b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapVO.java index db5faf669ffa..491dccc8d80a 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapVO.java @@ -25,12 +25,12 @@ @Entity @Table(name = "direct_download_certificate_host_map") -public class DirectDownloadCertificateHostMapVO { +public class DirectDownloadCertificateHostMapVO implements DirectDownloadCertificateHostMap{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") - private Long id; + private long id; @Column(name = "host_id") private Long hostId; @@ -50,15 +50,15 @@ public DirectDownloadCertificateHostMapVO(Long certificateId, Long hostId) { this.revoked = false; } - public Long getId() { + public long getId() { return id; } - public void setId(Long id) { + public void setId(long id) { this.id = id; } - public Long getHostId() { + public long getHostId() { return hostId; } @@ -66,7 +66,7 @@ public void setHostId(Long hostId) { this.hostId = hostId; } - public Long getCertificateId() { + public long getCertificateId() { return certificateId; } @@ -74,8 +74,8 @@ public void setCertificateId(Long certificateId) { this.certificateId = certificateId; } - public Boolean isRevoked() { - return revoked; + public boolean isRevoked() { + return revoked != null && revoked; } public void setRevoked(Boolean revoked) { diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 7d8fa9c849b0..1a1966bffa98 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -68,6 +68,7 @@ import org.apache.cloudstack.api.response.CreateCmdResponse; import org.apache.cloudstack.api.response.CreateSSHKeyPairResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; +import org.apache.cloudstack.api.response.DirectDownloadCertificateHostMapResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.DomainRouterResponse; @@ -161,6 +162,7 @@ import org.apache.cloudstack.backup.dao.BackupOfferingDao; import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -4555,6 +4557,7 @@ public DirectDownloadCertificateResponse createDirectDownloadCertificateResponse DataCenterVO datacenter = ApiDBUtils.findZoneById(certificate.getZoneId()); if (datacenter != null) { response.setZoneId(datacenter.getUuid()); + response.setZoneName(datacenter.getName()); } response.setId(certificate.getUuid()); response.setAlias(certificate.getAlias()); @@ -4563,4 +4566,24 @@ public DirectDownloadCertificateResponse createDirectDownloadCertificateResponse response.setObjectName("directdownloadcertificate"); return response; } + + @Override + public List createDirectDownloadCertificateHostMapResponse(List hostMappings) { + if (CollectionUtils.isEmpty(hostMappings)) { + return new ArrayList<>(); + } + List responses = new ArrayList<>(hostMappings.size()); + for (DirectDownloadCertificateHostMap map : hostMappings) { + DirectDownloadCertificateHostMapResponse response = new DirectDownloadCertificateHostMapResponse(); + HostVO host = ApiDBUtils.findHostById(map.getHostId()); + if (host != null) { + response.setHostId(host.getUuid()); + response.setHostName(host.getName()); + } + response.setRevoked(map.isRevoked()); + response.setObjectName("directdownloadcertificatehostmap"); + responses.add(response); + } + return responses; + } } diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index a6dcc5a61c63..7850c4087c32 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -620,6 +620,14 @@ public List listDirectDownloadCertificates(Long certi return certificates; } + @Override + public List getCertificateHostsMapping(Long certificateId) { + if (certificateId == null) { + throw new InvalidParameterValueException("Please specify a certificate ID"); + } + return new LinkedList<>(directDownloadCertificateHostMapDao.listByCertificateId(certificateId)); + } + protected boolean revokeCertificateAliasFromHost(String alias, Long hostId) { RevokeDirectDownloadCertificateCommand cmd = new RevokeDirectDownloadCertificateCommand(alias); try { From 7401183838b38b8db4f76ee07621820d9dd19f65 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Tue, 15 Mar 2022 14:07:00 -0300 Subject: [PATCH 09/22] Improvements on revoke certificate --- ...eTemplateDirectDownloadCertificateCmd.java | 18 ++---- .../download/DirectDownloadManager.java | 4 +- .../download/DirectDownloadManagerImpl.java | 64 +++++++++++++------ 3 files changed, 53 insertions(+), 33 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java index ef9fa8b1fa28..fc99b1e25565 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java @@ -30,6 +30,7 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.ZoneResponse; @@ -54,13 +55,10 @@ public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd { private static final Logger LOG = Logger.getLogger(RevokeTemplateDirectDownloadCertificateCmd.class); public static final String APINAME = "revokeTemplateDirectDownloadCertificate"; - @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, required = true, - description = "alias of the SSL certificate") - private String certificateAlias; - - @Parameter(name = ApiConstants.HYPERVISOR, type = BaseCmd.CommandType.STRING, required = true, - description = "hypervisor type") - private String hypervisor; + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, + entityType = DirectDownloadCertificateResponse.class, + description = "id of the certificate") + private Long certificateId; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "zone to revoke certificate", required = true) @@ -72,13 +70,9 @@ public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { - if (!hypervisor.equalsIgnoreCase("kvm")) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Currently supporting KVM hosts only"); - } SuccessResponse response = new SuccessResponse(getCommandName()); try { - LOG.debug("Revoking certificate " + certificateAlias + " from " + hypervisor + " hosts"); - boolean result = directDownloadManager.revokeCertificateAlias(certificateAlias, hypervisor, zoneId, hostId); + boolean result = directDownloadManager.revokeCertificate(certificateId, zoneId, hostId); response.setSuccess(result); setResponseObject(response); } catch (Exception e) { diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java index b00058136bd9..ff7f79f2528f 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java @@ -58,9 +58,9 @@ public interface DirectDownloadManager extends DirectDownloadService, PluggableS true); /** - * Revoke direct download certificate with alias 'alias' from hosts of hypervisor type 'hypervisor' + * Revoke direct download certificate from the hosts in the zone or a specific host */ - boolean revokeCertificateAlias(String certificateAlias, String hypervisor, Long zoneId, Long hostId); + boolean revokeCertificate(Long certificateId, Long zoneId, Long hostId); List listDirectDownloadCertificates(Long certificateId, Long zoneId); diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index 7850c4087c32..87eae2b7d0b3 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -567,40 +567,66 @@ public boolean syncCertificatesToHost(long hostId, long zoneId) { return syncCertificatesResult; } - @Override - public boolean revokeCertificateAlias(String certificateAlias, String hypervisor, Long zoneId, Long hostId) { - HypervisorType hypervisorType = HypervisorType.getType(hypervisor); - DirectDownloadCertificateVO certificateVO = directDownloadCertificateDao.findByAlias(certificateAlias, hypervisorType, zoneId); - if (certificateVO == null) { - throw new CloudRuntimeException("Certificate alias " + certificateAlias + " does not exist"); - } - - List maps = null; + private List getCertificateHostMappings(DirectDownloadCertificateVO certificateVO, Long hostId) { + List maps; if (hostId == null) { - maps = directDownloadCertificateHostMapDao.listByCertificateId(certificateVO.getId()); + maps = directDownloadCertificateHostMapDao.listByCertificateId(certificateVO.getId()); } else { DirectDownloadCertificateHostMapVO hostMap = directDownloadCertificateHostMapDao.findByCertificateAndHost(certificateVO.getId(), hostId); if (hostMap == null) { - s_logger.info("Certificate " + certificateAlias + " cannot be revoked from host " + hostId + " as it is not available on the host"); - return false; + String msg = "Certificate " + certificateVO.getAlias() + " cannot be revoked from host " + hostId + " as it is not available on the host"; + s_logger.error(msg); + throw new CloudRuntimeException(msg); } maps = Collections.singletonList(hostMap); } + return maps; + } + @Override + public boolean revokeCertificate(Long certificateId, Long zoneId, Long hostId) { + DirectDownloadCertificateVO certificateVO = directDownloadCertificateDao.findById(certificateId); + if (certificateVO == null) { + throw new CloudRuntimeException("Certificate with ID " + certificateId + " does not exist"); + } + String certificateAlias = certificateVO.getAlias(); + if (certificateVO.getZoneId().equals(zoneId)) { + throw new CloudRuntimeException("The certificate with alias " + certificateAlias + " was uploaded " + + " to the zone with ID=" + certificateVO.getZoneId() + " instead of the zone with ID=" + zoneId); + } + + List maps = getCertificateHostMappings(certificateVO, hostId); + if (CollectionUtils.isEmpty(maps)) { + return true; + } + + int success = 0; + int failed = 0; + int skipped = 0; s_logger.info("Attempting to revoke certificate alias: " + certificateAlias + " from " + maps.size() + " hosts"); - if (CollectionUtils.isNotEmpty(maps)) { - for (DirectDownloadCertificateHostMapVO map : maps) { - Long mappingHostId = map.getHostId(); - if (!revokeCertificateAliasFromHost(certificateAlias, mappingHostId)) { - String msg = "Could not revoke certificate from host: " + mappingHostId; - s_logger.error(msg); - throw new CloudRuntimeException(msg); + for (DirectDownloadCertificateHostMapVO map : maps) { + Long mappingHostId = map.getHostId(); + HostVO host = hostDao.findById(mappingHostId); + if (host == null || host.getDataCenterId() != zoneId || host.getHypervisorType() != HypervisorType.KVM) { + if (host != null) { + s_logger.debug("Skipping host " + host.getName() + " since its not on the zone " + zoneId + " or not a KVM host"); } + skipped++; + continue; + } + if (!revokeCertificateAliasFromHost(certificateAlias, mappingHostId)) { + String msg = "Could not revoke certificate from host: " + mappingHostId; + s_logger.error(msg); + failed++; + } else { s_logger.info("Certificate " + certificateAlias + " revoked from host " + mappingHostId); map.setRevoked(true); + success++; directDownloadCertificateHostMapDao.update(map.getId(), map); } } + s_logger.info(String.format("Certificate alias %s revoked from: %d hosts, %d failed, %d skipped", + certificateAlias, success, failed, skipped)); return true; } From 2b3851e2701c4d8a2fd6a240acf338018811ee4b Mon Sep 17 00:00:00 2001 From: nvazquez Date: Tue, 15 Mar 2022 23:57:49 -0300 Subject: [PATCH 10/22] Refactor revoke certificate API --- .../cloudstack/api/ResponseGenerator.java | 4 + ...eTemplateDirectDownloadCertificateCmd.java | 38 +++++++--- ...vokeDirectDownloadCertificateResponse.java | 73 +++++++++++++++++++ .../download/DirectDownloadManager.java | 31 +++++++- .../java/com/cloud/api/ApiResponseHelper.java | 18 +++++ .../download/DirectDownloadManagerImpl.java | 31 +++++--- 6 files changed, 174 insertions(+), 21 deletions(-) create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/RevokeDirectDownloadCertificateResponse.java diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 34d5397860b7..d3f0528981ca 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -26,11 +26,13 @@ import org.apache.cloudstack.api.response.DirectDownloadCertificateHostMapResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.cloudstack.api.response.RevokeDirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import com.cloud.resource.RollingMaintenanceManager; import org.apache.cloudstack.api.response.RollingMaintenanceResponse; import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; +import org.apache.cloudstack.direct.download.DirectDownloadManager; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -498,4 +500,6 @@ List createTemplateResponses(ResponseView view, VirtualMachine DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate); List createDirectDownloadCertificateHostMapResponse(List hostMappings); + + RevokeDirectDownloadCertificateResponse createDirectDownloadCertificateRevokeResponse(DirectDownloadManager.HostCertificateRevoke status); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java index fc99b1e25565..7ed91f52f532 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java @@ -28,26 +28,29 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.RevokeDirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.direct.download.DirectDownloadManager; +import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke; import org.apache.log4j.Logger; import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; @APICommand(name = RevokeTemplateDirectDownloadCertificateCmd.APINAME, - description = "Revoke a certificate alias from a KVM host", - responseObject = SuccessResponse.class, - requestHasSensitiveInfo = true, - responseHasSensitiveInfo = true, + description = "Revoke a certificate from hosts in a zone", + responseObject = RevokeDirectDownloadCertificateResponse.class, since = "4.13", authorized = {RoleType.Admin}) -public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd { +public class RevokeTemplateDirectDownloadCertificateCmd extends BaseListCmd { @Inject DirectDownloadManager directDownloadManager; @@ -68,15 +71,28 @@ public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd { description = "(optional) the host ID to revoke certificate") private Long hostId; + private void createResponse(final List hostsRevokeStatusList) { + final ListResponse response = new ListResponse<>(); + final List responses = new ArrayList<>(); + for (final HostCertificateRevoke status : hostsRevokeStatusList) { + if (status == null) { + continue; + } + RevokeDirectDownloadCertificateResponse revokeResponse = _responseGenerator.createDirectDownloadCertificateRevokeResponse(status); + responses.add(revokeResponse); + } + response.setResponses(responses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { - SuccessResponse response = new SuccessResponse(getCommandName()); try { - boolean result = directDownloadManager.revokeCertificate(certificateId, zoneId, hostId); - response.setSuccess(result); - setResponseObject(response); + List hostsResult = directDownloadManager.revokeCertificate(certificateId, zoneId, hostId); + createResponse(hostsResult); } catch (Exception e) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed revoking certificate: " + e.getMessage()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RevokeDirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RevokeDirectDownloadCertificateResponse.java new file mode 100644 index 000000000000..f8805ce971bc --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/RevokeDirectDownloadCertificateResponse.java @@ -0,0 +1,73 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +public class RevokeDirectDownloadCertificateResponse extends BaseResponse { + + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "the ID of the host") + private String hostId; + + @SerializedName(ApiConstants.HOST_NAME) + @Param(description = "the name of the host") + private String hostName; + + @SerializedName(ApiConstants.STATUS) + @Param(description = "indicates if the certificate has been revoked from the host, failed or skipped") + private String status; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "indicates the details in case of failure or host skipped") + private String details; + + public String getHostId() { + return hostId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getDetails() { + return details; + } + + public void setDetails(String details) { + this.details = details; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java index ff7f79f2528f..a4e9817c218e 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.direct.download; +import com.cloud.host.Host; import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadService; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; @@ -57,10 +58,38 @@ public interface DirectDownloadManager extends DirectDownloadService, PluggableS "Requesting a connection from connection manager timeout in milliseconds for direct download", true); + class HostCertificateRevoke { + public enum CertificateStatus { + REVOKED, FAILED, SKIPPED + } + + HostCertificateRevoke(CertificateStatus status, Host host, String details) { + this.status = status; + this.host = host; + this.details = details; + } + + private CertificateStatus status; + private Host host; + private String details; + + public CertificateStatus getStatus() { + return status; + } + + public Host getHost() { + return host; + } + + public String getDetails() { + return details; + } + } + /** * Revoke direct download certificate from the hosts in the zone or a specific host */ - boolean revokeCertificate(Long certificateId, Long zoneId, Long hostId); + List revokeCertificate(Long certificateId, Long zoneId, Long hostId); List listDirectDownloadCertificates(Long certificateId, Long zoneId); diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 1a1966bffa98..7015acf5ea2a 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -120,6 +120,7 @@ import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.ResourceLimitResponse; import org.apache.cloudstack.api.response.ResourceTagResponse; +import org.apache.cloudstack.api.response.RevokeDirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.RollingMaintenanceHostSkippedResponse; import org.apache.cloudstack.api.response.RollingMaintenanceHostUpdatedResponse; import org.apache.cloudstack.api.response.RollingMaintenanceResponse; @@ -163,6 +164,8 @@ import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; +import org.apache.cloudstack.direct.download.DirectDownloadManager; +import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke.CertificateStatus; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -4586,4 +4589,19 @@ public List createDirectDownloadCertif } return responses; } + + @Override + public RevokeDirectDownloadCertificateResponse createDirectDownloadCertificateRevokeResponse(DirectDownloadManager.HostCertificateRevoke hostStatus) { + RevokeDirectDownloadCertificateResponse response = new RevokeDirectDownloadCertificateResponse(); + Host host = hostStatus.getHost(); + if (host != null) { + response.setHostId(host.getUuid()); + response.setHostName(host.getName()); + } + CertificateStatus status = hostStatus.getStatus(); + response.setStatus(status.name()); + response.setDetails(hostStatus.getDetails()); + response.setObjectName("revokedirectdownloadcertificate"); + return response; + } } diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index 87eae2b7d0b3..f770c65b8b53 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -19,6 +19,9 @@ package org.apache.cloudstack.direct.download; import static com.cloud.storage.Storage.ImageFormat; +import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke.CertificateStatus.FAILED; +import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke.CertificateStatus.REVOKED; +import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke.CertificateStatus.SKIPPED; import java.net.URI; import java.net.URISyntaxException; @@ -41,6 +44,7 @@ import javax.naming.ConfigurationException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.Pair; import org.apache.cloudstack.agent.directdownload.DirectDownloadAnswer; import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand; import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand.DownloadProtocol; @@ -584,7 +588,7 @@ private List getCertificateHostMappings(Dire } @Override - public boolean revokeCertificate(Long certificateId, Long zoneId, Long hostId) { + public List revokeCertificate(Long certificateId, Long zoneId, Long hostId) { DirectDownloadCertificateVO certificateVO = directDownloadCertificateDao.findById(certificateId); if (certificateVO == null) { throw new CloudRuntimeException("Certificate with ID " + certificateId + " does not exist"); @@ -595,9 +599,10 @@ public boolean revokeCertificate(Long certificateId, Long zoneId, Long hostId) { " to the zone with ID=" + certificateVO.getZoneId() + " instead of the zone with ID=" + zoneId); } + List hostsList = new ArrayList<>(); List maps = getCertificateHostMappings(certificateVO, hostId); if (CollectionUtils.isEmpty(maps)) { - return true; + return hostsList; } int success = 0; @@ -607,27 +612,35 @@ public boolean revokeCertificate(Long certificateId, Long zoneId, Long hostId) { for (DirectDownloadCertificateHostMapVO map : maps) { Long mappingHostId = map.getHostId(); HostVO host = hostDao.findById(mappingHostId); + HostCertificateRevoke hostStatus; if (host == null || host.getDataCenterId() != zoneId || host.getHypervisorType() != HypervisorType.KVM) { if (host != null) { - s_logger.debug("Skipping host " + host.getName() + " since its not on the zone " + zoneId + " or not a KVM host"); + String reason = host.getDataCenterId() != zoneId ? "Host is not in the zone " + zoneId : "Host hypervisor is not KVM"; + s_logger.debug("Skipping host " + host.getName() + ": " + reason); + hostStatus = new HostCertificateRevoke(SKIPPED, host, reason); + hostsList.add(hostStatus); } skipped++; continue; } - if (!revokeCertificateAliasFromHost(certificateAlias, mappingHostId)) { - String msg = "Could not revoke certificate from host: " + mappingHostId; + Pair result = revokeCertificateAliasFromHost(certificateAlias, mappingHostId); + if (!result.first()) { + String msg = "Could not revoke certificate from host: " + mappingHostId + ": " + result.second(); s_logger.error(msg); + hostStatus = new HostCertificateRevoke(FAILED, host, result.second()); failed++; } else { s_logger.info("Certificate " + certificateAlias + " revoked from host " + mappingHostId); map.setRevoked(true); + hostStatus = new HostCertificateRevoke(REVOKED, host, null); success++; directDownloadCertificateHostMapDao.update(map.getId(), map); } + hostsList.add(hostStatus); } s_logger.info(String.format("Certificate alias %s revoked from: %d hosts, %d failed, %d skipped", certificateAlias, success, failed, skipped)); - return true; + return hostsList; } @Override @@ -654,15 +667,15 @@ public List getCertificateHostsMapping(Long ce return new LinkedList<>(directDownloadCertificateHostMapDao.listByCertificateId(certificateId)); } - protected boolean revokeCertificateAliasFromHost(String alias, Long hostId) { + protected Pair revokeCertificateAliasFromHost(String alias, Long hostId) { RevokeDirectDownloadCertificateCommand cmd = new RevokeDirectDownloadCertificateCommand(alias); try { Answer answer = agentManager.send(hostId, cmd); - return answer != null && answer.getResult(); + return new Pair<>(answer != null && answer.getResult(), answer != null ? answer.getDetails() : ""); } catch (AgentUnavailableException | OperationTimedoutException e) { s_logger.error("Error revoking certificate " + alias + " from host " + hostId, e); + return new Pair<>(false, e.getMessage()); } - return false; } @Override From cee4179180930c737bb617a98dd50b5dbbccaad0 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Wed, 16 Mar 2022 09:38:14 -0300 Subject: [PATCH 11/22] Fix condition --- .../cloudstack/direct/download/DirectDownloadManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index f770c65b8b53..2178d34db8b7 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -594,7 +594,7 @@ public List revokeCertificate(Long certificateId, Long zo throw new CloudRuntimeException("Certificate with ID " + certificateId + " does not exist"); } String certificateAlias = certificateVO.getAlias(); - if (certificateVO.getZoneId().equals(zoneId)) { + if (!certificateVO.getZoneId().equals(zoneId)) { throw new CloudRuntimeException("The certificate with alias " + certificateAlias + " was uploaded " + " to the zone with ID=" + certificateVO.getZoneId() + " instead of the zone with ID=" + zoneId); } From 8ef8ec86dac6f496d15918a3ca5c25680cc9fe11 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Wed, 16 Mar 2022 10:36:05 -0300 Subject: [PATCH 12/22] Filter only certificates not revoked for revokeCertificate API --- .../download/DirectDownloadCertificateHostMapDao.java | 1 + .../DirectDownloadCertificateHostMapDaoImpl.java | 9 +++++++++ .../direct/download/DirectDownloadManagerImpl.java | 5 ++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapDao.java b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapDao.java index e119b1d491ed..fa056f374c96 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapDao.java @@ -23,4 +23,5 @@ public interface DirectDownloadCertificateHostMapDao extends GenericDao { DirectDownloadCertificateHostMapVO findByCertificateAndHost(long certificateId, long hostId); List listByCertificateId(long certificateId); + List listByCertificateIdAndRevoked(long certificateId, boolean revoked); } \ No newline at end of file diff --git a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapDaoImpl.java index 7a0b732bbfd4..de895cb6f98d 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapDaoImpl.java @@ -29,6 +29,7 @@ public DirectDownloadCertificateHostMapDaoImpl() { mapSearchBuilder = createSearchBuilder(); mapSearchBuilder.and("certificate_id", mapSearchBuilder.entity().getCertificateId(), SearchCriteria.Op.EQ); mapSearchBuilder.and("host_id", mapSearchBuilder.entity().getHostId(), SearchCriteria.Op.EQ); + mapSearchBuilder.and("revoked", mapSearchBuilder.entity().isRevoked(), SearchCriteria.Op.EQ); mapSearchBuilder.done(); } @Override @@ -45,4 +46,12 @@ public List listByCertificateId(long certifi sc.setParameters("certificate_id", certificateId); return listBy(sc); } + + @Override + public List listByCertificateIdAndRevoked(long certificateId, boolean revoked) { + SearchCriteria sc = mapSearchBuilder.create(); + sc.setParameters("certificate_id", certificateId); + sc.setParameters("revoked", revoked); + return listBy(sc); + } } \ No newline at end of file diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index 2178d34db8b7..5954ea482288 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -574,13 +574,16 @@ public boolean syncCertificatesToHost(long hostId, long zoneId) { private List getCertificateHostMappings(DirectDownloadCertificateVO certificateVO, Long hostId) { List maps; if (hostId == null) { - maps = directDownloadCertificateHostMapDao.listByCertificateId(certificateVO.getId()); + maps = directDownloadCertificateHostMapDao.listByCertificateIdAndRevoked(certificateVO.getId(), false); } else { DirectDownloadCertificateHostMapVO hostMap = directDownloadCertificateHostMapDao.findByCertificateAndHost(certificateVO.getId(), hostId); if (hostMap == null) { String msg = "Certificate " + certificateVO.getAlias() + " cannot be revoked from host " + hostId + " as it is not available on the host"; s_logger.error(msg); throw new CloudRuntimeException(msg); + } else if (hostMap.isRevoked()) { + s_logger.debug("Certificate " + certificateVO.getAlias() + " was already revoked from host " + hostId + " skipping it"); + return new LinkedList<>(); } maps = Collections.singletonList(hostMap); } From 1cddc3924a2df2a9ace3216594eeefefbb5d1bc7 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Wed, 16 Mar 2022 11:43:33 -0300 Subject: [PATCH 13/22] Improve upload certificate and add provision certificate API --- .../cloudstack/api/ResponseGenerator.java | 4 +- ...nTemplateDirectDownloadCertificateCmd.java | 83 ++++++++++++++ ...eTemplateDirectDownloadCertificateCmd.java | 19 ++-- ...dTemplateDirectDownloadCertificateCmd.java | 37 ++++-- ...ownloadCertificateHostStatusResponse.java} | 2 +- .../download/DirectDownloadManager.java | 14 ++- framework/direct-download/pom.xml | 8 ++ .../download/DirectDownloadService.java | 9 +- .../java/com/cloud/api/ApiResponseHelper.java | 10 +- .../download/DirectDownloadManagerImpl.java | 107 ++++++++++++------ 10 files changed, 219 insertions(+), 74 deletions(-) create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java rename api/src/main/java/org/apache/cloudstack/api/response/{RevokeDirectDownloadCertificateResponse.java => DirectDownloadCertificateHostStatusResponse.java} (96%) diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index d3f0528981ca..b7c564e0eb4c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -26,7 +26,7 @@ import org.apache.cloudstack.api.response.DirectDownloadCertificateHostMapResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ResourceIconResponse; -import org.apache.cloudstack.api.response.RevokeDirectDownloadCertificateResponse; +import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import com.cloud.resource.RollingMaintenanceManager; import org.apache.cloudstack.api.response.RollingMaintenanceResponse; @@ -501,5 +501,5 @@ List createTemplateResponses(ResponseView view, VirtualMachine List createDirectDownloadCertificateHostMapResponse(List hostMappings); - RevokeDirectDownloadCertificateResponse createDirectDownloadCertificateRevokeResponse(DirectDownloadManager.HostCertificateRevoke status); + DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus status, String objectName); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java new file mode 100644 index 000000000000..f733affddded --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java @@ -0,0 +1,83 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package org.apache.cloudstack.api.command.admin.direct.download; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.utils.Pair; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.direct.download.DirectDownloadManager; + +import javax.inject.Inject; + +@APICommand(name = ProvisionTemplateDirectDownloadCertificateCmd.APINAME, + description = "Provisions a host with a direct download certificate", + responseObject = SuccessResponse.class, + since = "4.17.0", + authorized = {RoleType.Admin}) +public class ProvisionTemplateDirectDownloadCertificateCmd extends BaseCmd { + + public static final String APINAME = "provisionTemplateDirectDownloadCertificates"; + + @Inject + DirectDownloadManager directDownloadManager; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class, + description = "the id of the direct download certificate to provision") + private Long id; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, + description = "the host to provision the certificate") + private Long hostId; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + Pair result = directDownloadManager.provisionCertificate(id, hostId); + if (result.first()) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setDisplayText(result.second()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to provision certificate: " + result.second()); + } + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java index 7ed91f52f532..465daf6290a4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java @@ -34,11 +34,11 @@ import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.api.response.RevokeDirectDownloadCertificateResponse; +import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.direct.download.DirectDownloadManager; -import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke; +import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus; import org.apache.log4j.Logger; import javax.inject.Inject; @@ -47,7 +47,7 @@ @APICommand(name = RevokeTemplateDirectDownloadCertificateCmd.APINAME, description = "Revoke a certificate from hosts in a zone", - responseObject = RevokeDirectDownloadCertificateResponse.class, + responseObject = DirectDownloadCertificateHostStatusResponse.class, since = "4.13", authorized = {RoleType.Admin}) public class RevokeTemplateDirectDownloadCertificateCmd extends BaseListCmd { @@ -71,14 +71,15 @@ public class RevokeTemplateDirectDownloadCertificateCmd extends BaseListCmd { description = "(optional) the host ID to revoke certificate") private Long hostId; - private void createResponse(final List hostsRevokeStatusList) { - final ListResponse response = new ListResponse<>(); - final List responses = new ArrayList<>(); - for (final HostCertificateRevoke status : hostsRevokeStatusList) { + private void createResponse(final List hostsRevokeStatusList) { + final ListResponse response = new ListResponse<>(); + final List responses = new ArrayList<>(); + for (final HostCertificateStatus status : hostsRevokeStatusList) { if (status == null) { continue; } - RevokeDirectDownloadCertificateResponse revokeResponse = _responseGenerator.createDirectDownloadCertificateRevokeResponse(status); + DirectDownloadCertificateHostStatusResponse revokeResponse = + _responseGenerator.createDirectDownloadCertificateHostStatusResponse(status, "revoketemplatedirectdownloadcertificate"); responses.add(revokeResponse); } response.setResponses(responses); @@ -89,7 +90,7 @@ private void createResponse(final List hostsRevokeStatusL @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - List hostsResult = directDownloadManager.revokeCertificate(certificateId, zoneId, hostId); + List hostsResult = directDownloadManager.revokeCertificate(certificateId, zoneId, hostId); createResponse(hostsResult); } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed revoking certificate: " + e.getMessage()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java index 223f20b5bb0d..d70327612498 100755 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java @@ -20,26 +20,29 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.direct.download.DirectDownloadManager; +import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus; import org.apache.log4j.Logger; import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; @APICommand(name = UploadTemplateDirectDownloadCertificateCmd.APINAME, description = "Upload a certificate for HTTPS direct template download on KVM hosts", - responseObject = SuccessResponse.class, - requestHasSensitiveInfo = true, - responseHasSensitiveInfo = true, + responseObject = DirectDownloadCertificateHostStatusResponse.class, since = "4.11.0", authorized = {RoleType.Admin}) -public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd { +public class UploadTemplateDirectDownloadCertificateCmd extends BaseListCmd { @Inject DirectDownloadManager directDownloadManager; @@ -63,9 +66,25 @@ public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd { private Long zoneId; @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, - description = "(optional) the host ID to revoke certificate") + description = "(optional) the host ID to upload certificate") private Long hostId; + private void createResponse(final List hostStatusList) { + final ListResponse response = new ListResponse<>(); + final List responses = new ArrayList<>(); + for (final HostCertificateStatus status : hostStatusList) { + if (status == null) { + continue; + } + DirectDownloadCertificateHostStatusResponse revokeResponse = + _responseGenerator.createDirectDownloadCertificateHostStatusResponse(status, "uploadtemplatedirectdownloadcertificate"); + responses.add(revokeResponse); + } + response.setResponses(responses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + @Override public void execute() { if (!hypervisor.equalsIgnoreCase("kvm")) { @@ -74,10 +93,8 @@ public void execute() { try { LOG.debug("Uploading certificate " + name + " to agents for Direct Download"); - boolean result = directDownloadManager.uploadCertificateToHosts(certificate, name, hypervisor, zoneId, hostId); - SuccessResponse response = new SuccessResponse(getCommandName()); - response.setSuccess(result); - setResponseObject(response); + List hostStatus = directDownloadManager.uploadCertificateToHosts(certificate, name, hypervisor, zoneId, hostId); + createResponse(hostStatus); } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RevokeDirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostStatusResponse.java similarity index 96% rename from api/src/main/java/org/apache/cloudstack/api/response/RevokeDirectDownloadCertificateResponse.java rename to api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostStatusResponse.java index f8805ce971bc..cc9f2fc366ad 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RevokeDirectDownloadCertificateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostStatusResponse.java @@ -21,7 +21,7 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; -public class RevokeDirectDownloadCertificateResponse extends BaseResponse { +public class DirectDownloadCertificateHostStatusResponse extends BaseResponse { @SerializedName(ApiConstants.HOST_ID) @Param(description = "the ID of the host") diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java index a4e9817c218e..4e13f0ee8e4e 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java @@ -58,12 +58,12 @@ public interface DirectDownloadManager extends DirectDownloadService, PluggableS "Requesting a connection from connection manager timeout in milliseconds for direct download", true); - class HostCertificateRevoke { + class HostCertificateStatus { public enum CertificateStatus { - REVOKED, FAILED, SKIPPED + REVOKED, FAILED, SKIPPED, UPLOADED } - HostCertificateRevoke(CertificateStatus status, Host host, String details) { + HostCertificateStatus(CertificateStatus status, Host host, String details) { this.status = status; this.host = host; this.details = details; @@ -89,9 +89,15 @@ public String getDetails() { /** * Revoke direct download certificate from the hosts in the zone or a specific host */ - List revokeCertificate(Long certificateId, Long zoneId, Long hostId); + List revokeCertificate(Long certificateId, Long zoneId, Long hostId); List listDirectDownloadCertificates(Long certificateId, Long zoneId); List getCertificateHostsMapping(Long certificateId); + + /** + * Upload client certificate to each running host + * @return + */ + List uploadCertificateToHosts(String certificateCer, String certificateName, String hypervisor, Long zoneId, Long hostId); } diff --git a/framework/direct-download/pom.xml b/framework/direct-download/pom.xml index 03a37532e5b5..e2ce16e27e4d 100644 --- a/framework/direct-download/pom.xml +++ b/framework/direct-download/pom.xml @@ -21,6 +21,14 @@ 4.0.0 cloud-framework-direct-download Apache CloudStack Framework - Direct Download to Primary Storage + + + org.apache.cloudstack + cloud-utils + 4.17.0.0-SNAPSHOT + compile + + cloudstack-framework org.apache.cloudstack diff --git a/framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadService.java b/framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadService.java index 983f935a2fa9..6aeb16b5d85f 100644 --- a/framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadService.java +++ b/framework/direct-download/src/main/java/org/apache/cloudstack/framework/agent/direct/download/DirectDownloadService.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.framework.agent.direct.download; +import com.cloud.utils.Pair; + public interface DirectDownloadService { /** @@ -24,15 +26,10 @@ public interface DirectDownloadService { */ void downloadTemplate(long templateId, long poolId, long hostId); - /** - * Upload client certificate to each running host - */ - boolean uploadCertificateToHosts(String certificateCer, String certificateName, String hypervisor, Long zoneId, Long hostId); - /** * Upload a stored certificate on database with id 'certificateId' to host with id 'hostId' */ - boolean uploadCertificate(long certificateId, long hostId); + Pair provisionCertificate(long certificateId, long hostId); /** * Sync the stored certificates to host with id 'hostId' diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 7015acf5ea2a..72d5fa351f8d 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -120,7 +120,7 @@ import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.ResourceLimitResponse; import org.apache.cloudstack.api.response.ResourceTagResponse; -import org.apache.cloudstack.api.response.RevokeDirectDownloadCertificateResponse; +import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; import org.apache.cloudstack.api.response.RollingMaintenanceHostSkippedResponse; import org.apache.cloudstack.api.response.RollingMaintenanceHostUpdatedResponse; import org.apache.cloudstack.api.response.RollingMaintenanceResponse; @@ -165,7 +165,7 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.direct.download.DirectDownloadManager; -import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke.CertificateStatus; +import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -4591,8 +4591,8 @@ public List createDirectDownloadCertif } @Override - public RevokeDirectDownloadCertificateResponse createDirectDownloadCertificateRevokeResponse(DirectDownloadManager.HostCertificateRevoke hostStatus) { - RevokeDirectDownloadCertificateResponse response = new RevokeDirectDownloadCertificateResponse(); + public DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus hostStatus, String objectName) { + DirectDownloadCertificateHostStatusResponse response = new DirectDownloadCertificateHostStatusResponse(); Host host = hostStatus.getHost(); if (host != null) { response.setHostId(host.getUuid()); @@ -4601,7 +4601,7 @@ public RevokeDirectDownloadCertificateResponse createDirectDownloadCertificateRe CertificateStatus status = hostStatus.getStatus(); response.setStatus(status.name()); response.setDetails(hostStatus.getDetails()); - response.setObjectName("revokedirectdownloadcertificate"); + response.setObjectName(objectName); return response; } } diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index 5954ea482288..a935c33ef6b2 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -19,9 +19,10 @@ package org.apache.cloudstack.direct.download; import static com.cloud.storage.Storage.ImageFormat; -import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke.CertificateStatus.FAILED; -import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke.CertificateStatus.REVOKED; -import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateRevoke.CertificateStatus.SKIPPED; +import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus.FAILED; +import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus.REVOKED; +import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus.SKIPPED; +import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus.UPLOADED; import java.net.URI; import java.net.URISyntaxException; @@ -454,7 +455,7 @@ protected void certificateSanity(String certificatePem) { } @Override - public boolean uploadCertificateToHosts(String certificateCer, String alias, String hypervisor, Long zoneId, Long hostId) { + public List uploadCertificateToHosts(String certificateCer, String alias, String hypervisor, Long zoneId, Long hostId) { if (alias != null && (alias.equalsIgnoreCase("cloud") || alias.startsWith("cloudca"))) { throw new CloudRuntimeException("Please provide a different alias name for the certificate"); } @@ -463,6 +464,10 @@ public boolean uploadCertificateToHosts(String certificateCer, String alias, Str DirectDownloadCertificateVO certificateVO; HypervisorType hypervisorType = HypervisorType.getType(hypervisor); + if (hypervisorType != HypervisorType.KVM) { + throw new CloudRuntimeException("Direct download certificates only supported on KVM"); + } + if (hostId == null) { hosts = getRunningHostsToUploadCertificate(zoneId, hypervisorType); @@ -481,48 +486,55 @@ public boolean uploadCertificateToHosts(String certificateCer, String alias, Str certificateVO = directDownloadCertificateDao.findByAlias(alias, hypervisorType, zoneId); if (certificateVO == null) { s_logger.info("Certificate must be uploaded on zone " + zoneId); - return false; + return new ArrayList<>(); } } s_logger.info("Attempting to upload certificate: " + alias + " to " + hosts.size() + " hosts on zone " + zoneId); - int hostCount = 0; + int success = 0; + int failed = 0; + List results = new ArrayList<>(); if (CollectionUtils.isNotEmpty(hosts)) { for (HostVO host : hosts) { - if (!uploadCertificate(certificateVO.getId(), host.getId())) { - String msg = "Could not upload certificate " + alias + " on host: " + host.getName() + " (" + host.getUuid() + ")"; + if (host == null) { + continue; + } + HostCertificateStatus hostStatus; + Pair result = provisionCertificate(certificateVO.getId(), host.getId()); + if (!result.first()) { + String msg = "Could not upload certificate " + alias + " on host: " + host.getName() + " (" + host.getUuid() + "): " + result.second(); s_logger.error(msg); - throw new CloudRuntimeException(msg); + failed++; + hostStatus = new HostCertificateStatus(FAILED, host, result.second()); + } else { + success++; + hostStatus = new HostCertificateStatus(UPLOADED, host, ""); } - hostCount++; + results.add(hostStatus); } } - s_logger.info("Certificate was successfully uploaded to " + hostCount + " hosts"); - return true; + s_logger.info("Certificate was successfully uploaded to " + success + " hosts, " + failed + " failed"); + return results; } - /** - * Upload and import certificate to hostId on keystore - */ - public boolean uploadCertificate(long certificateId, long hostId) { - DirectDownloadCertificateVO certificateVO = directDownloadCertificateDao.findById(certificateId); - if (certificateVO == null) { - throw new CloudRuntimeException("Could not find certificate with id " + certificateId + " to upload to host: " + hostId); - } - - String certificate = certificateVO.getCertificate(); - String alias = certificateVO.getAlias(); + private Pair setupCertificateOnHost(DirectDownloadCertificate certificate, long hostId) { + String certificateStr = certificate.getCertificate(); + String alias = certificate.getAlias(); + long certificateId = certificate.getId(); - s_logger.debug("Uploading certificate: " + certificateVO.getAlias() + " to host " + hostId); - SetupDirectDownloadCertificateCommand cmd = new SetupDirectDownloadCertificateCommand(certificate, alias); + s_logger.debug("Uploading certificate: " + alias + " to host " + hostId); + SetupDirectDownloadCertificateCommand cmd = new SetupDirectDownloadCertificateCommand(certificateStr, alias); Answer answer = agentManager.easySend(hostId, cmd); + Pair result; if (answer == null || !answer.getResult()) { String msg = "Certificate " + alias + " could not be added to host " + hostId; if (answer != null) { msg += " due to: " + answer.getDetails(); } s_logger.error(msg); - return false; + result = new Pair<>(false, msg); + } else { + result = new Pair<>(true, "OK"); } s_logger.info("Certificate " + alias + " successfully uploaded to host: " + hostId); @@ -534,8 +546,25 @@ public boolean uploadCertificate(long certificateId, long hostId) { DirectDownloadCertificateHostMapVO mapVO = new DirectDownloadCertificateHostMapVO(certificateId, hostId); directDownloadCertificateHostMapDao.persist(mapVO); } + return result; + } + /** + * Upload and import certificate to hostId on keystore + */ + public Pair provisionCertificate(long certificateId, long hostId) { + DirectDownloadCertificateVO certificateVO = directDownloadCertificateDao.findById(certificateId); + if (certificateVO == null) { + throw new CloudRuntimeException("Could not find certificate with id " + certificateId + " to upload to host: " + hostId); + } + HostVO host = hostDao.findById(hostId); + if (host == null) { + throw new CloudRuntimeException("Cannot find a host with ID " + hostId); + } + if (host.getHypervisorType() != HypervisorType.KVM) { + throw new CloudRuntimeException("Cannot provision certificate to host " + host.getName() + " since it is not KVM"); + } - return true; + return setupCertificateOnHost(certificateVO, hostId); } @Override @@ -555,8 +584,9 @@ public boolean syncCertificatesToHost(long hostId, long zoneId) { DirectDownloadCertificateHostMapVO mapping = directDownloadCertificateHostMapDao.findByCertificateAndHost(certificateVO.getId(), hostId); if (mapping == null) { s_logger.debug("Syncing certificate " + certificateVO.getId() + " (" + certificateVO.getAlias() + ") on host: " + hostId + ", uploading it"); - if (!uploadCertificate(certificateVO.getId(), hostId)) { - String msg = "Could not sync certificate " + certificateVO.getId() + " (" + certificateVO.getAlias() + ") on host: " + hostId + ", upload failed"; + Pair result = provisionCertificate(certificateVO.getId(), hostId); + if (!result.first()) { + String msg = "Could not sync certificate " + certificateVO.getId() + " (" + certificateVO.getAlias() + ") on host: " + hostId + ", upload failed: " + result.second(); s_logger.error(msg); syncCertificatesResult = false; } else { @@ -591,7 +621,7 @@ private List getCertificateHostMappings(Dire } @Override - public List revokeCertificate(Long certificateId, Long zoneId, Long hostId) { + public List revokeCertificate(Long certificateId, Long zoneId, Long hostId) { DirectDownloadCertificateVO certificateVO = directDownloadCertificateDao.findById(certificateId); if (certificateVO == null) { throw new CloudRuntimeException("Certificate with ID " + certificateId + " does not exist"); @@ -602,7 +632,7 @@ public List revokeCertificate(Long certificateId, Long zo " to the zone with ID=" + certificateVO.getZoneId() + " instead of the zone with ID=" + zoneId); } - List hostsList = new ArrayList<>(); + List hostsList = new ArrayList<>(); List maps = getCertificateHostMappings(certificateVO, hostId); if (CollectionUtils.isEmpty(maps)) { return hostsList; @@ -615,12 +645,12 @@ public List revokeCertificate(Long certificateId, Long zo for (DirectDownloadCertificateHostMapVO map : maps) { Long mappingHostId = map.getHostId(); HostVO host = hostDao.findById(mappingHostId); - HostCertificateRevoke hostStatus; + HostCertificateStatus hostStatus; if (host == null || host.getDataCenterId() != zoneId || host.getHypervisorType() != HypervisorType.KVM) { if (host != null) { String reason = host.getDataCenterId() != zoneId ? "Host is not in the zone " + zoneId : "Host hypervisor is not KVM"; s_logger.debug("Skipping host " + host.getName() + ": " + reason); - hostStatus = new HostCertificateRevoke(SKIPPED, host, reason); + hostStatus = new HostCertificateStatus(SKIPPED, host, reason); hostsList.add(hostStatus); } skipped++; @@ -630,12 +660,12 @@ public List revokeCertificate(Long certificateId, Long zo if (!result.first()) { String msg = "Could not revoke certificate from host: " + mappingHostId + ": " + result.second(); s_logger.error(msg); - hostStatus = new HostCertificateRevoke(FAILED, host, result.second()); + hostStatus = new HostCertificateStatus(FAILED, host, result.second()); failed++; } else { s_logger.info("Certificate " + certificateAlias + " revoked from host " + mappingHostId); map.setRevoked(true); - hostStatus = new HostCertificateRevoke(REVOKED, host, null); + hostStatus = new HostCertificateStatus(REVOKED, host, null); success++; directDownloadCertificateHostMapDao.update(map.getId(), map); } @@ -760,10 +790,13 @@ protected void runInContext() { s_logger.debug("Certificate " + certificateVO.getId() + " (" + certificateVO.getAlias() + ") was not uploaded to host: " + hostVO.getId() + " uploading it"); - boolean result = directDownloadManager.uploadCertificate(certificateVO.getId(), hostVO.getId()); + Pair result = directDownloadManager.provisionCertificate(certificateVO.getId(), hostVO.getId()); s_logger.debug("Certificate " + certificateVO.getAlias() + " " + - (result ? "uploaded" : "could not be uploaded") + + (result.first() ? "uploaded" : "could not be uploaded") + " to host " + hostVO.getId()); + if (!result.first()) { + s_logger.error("Certificate " + certificateVO.getAlias() + " failed: " + result.second()); + } } } } From 5f1a74b79cbf73aa5af5d31bfe7bf168e2b4a04b Mon Sep 17 00:00:00 2001 From: nvazquez Date: Thu, 24 Mar 2022 00:21:17 -0300 Subject: [PATCH 14/22] Improve certificate response output --- .../apache/cloudstack/api/ApiConstants.java | 4 ++ .../DirectDownloadCertificateResponse.java | 70 ++++++++++++++++--- .../java/com/cloud/api/ApiResponseHelper.java | 22 +++++- .../com/cloud/api/ApiResponseHelperTest.java | 43 ++++++++++++ 4 files changed, 127 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 00e893322b28..27722109cca6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -58,6 +58,10 @@ public class ApiConstants { public static final String CERTIFICATE_CHAIN = "certchain"; public static final String CERTIFICATE_FINGERPRINT = "fingerprint"; public static final String CERTIFICATE_ID = "certid"; + public static final String CERTIFICATE_ISSUER = "issuer"; + public static final String CERTIFICATE_SERIALNUM = "serialnum"; + public static final String CERTIFICATE_SUBJECT = "subject"; + public static final String CERTIFICATE_VALIDITY = "validity"; public static final String ENABLED_REVOCATION_CHECK = "enabledrevocationcheck"; public static final String CONTROLLER = "controller"; public static final String CONTROLLER_UNIT = "controllerunit"; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java index 7e3830636d5d..fdf21d8fb725 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java @@ -44,9 +44,25 @@ public class DirectDownloadCertificateResponse extends BaseResponse { @Param(description = "the zone name where the certificate is uploaded") private String zoneName; - @SerializedName(ApiConstants.CERTIFICATE) - @Param(description = "the direct download certificate") - private String certificate; + @SerializedName(ApiConstants.VERSION) + @Param(description = "the direct download certificate version") + private String version; + + @SerializedName(ApiConstants.CERTIFICATE_SUBJECT) + @Param(description = "the direct download certificate subject") + private String subject; + + @SerializedName(ApiConstants.CERTIFICATE_ISSUER) + @Param(description = "the direct download certificate issuer") + private String issuer; + + @SerializedName(ApiConstants.CERTIFICATE_VALIDITY) + @Param(description = "the direct download certificate issuer") + private String validity; + + @SerializedName(ApiConstants.CERTIFICATE_SERIALNUM) + @Param(description = "the direct download certificate serial num") + private String serialNum; @SerializedName(ApiConstants.HYPERVISOR) @Param(description = "the hypervisor of the hosts where the certificate is uploaded") @@ -80,14 +96,6 @@ public void setZoneId(String zoneId) { this.zoneId = zoneId; } - public String getCertificate() { - return certificate; - } - - public void setCertificate(String certificate) { - this.certificate = certificate; - } - public String getHypervisor() { return hypervisor; } @@ -111,4 +119,44 @@ public List getHostsMap() { public void setHostsMap(List hosts) { this.hostsMap = hosts; } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getIssuer() { + return issuer; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public String getValidity() { + return validity; + } + + public void setValidity(String validity) { + this.validity = validity; + } + + public String getSerialNum() { + return serialNum; + } + + public void setSerialNum(String serialNum) { + this.serialNum = serialNum; + } } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 72d5fa351f8d..f0ea0bd7c72b 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -18,6 +18,8 @@ import static com.cloud.utils.NumbersUtil.toHumanReadableSize; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; @@ -34,6 +36,7 @@ import javax.inject.Inject; +import com.cloud.utils.security.CertificateHelper; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; @@ -370,6 +373,7 @@ import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; import org.apache.commons.lang3.StringUtils; +import sun.security.x509.X509CertImpl; public class ApiResponseHelper implements ResponseGenerator { @@ -4554,6 +4558,22 @@ public ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon return ApiDBUtils.newResourceIconResponse(resourceIcon); } + protected void handleCertificateResponse(String certStr, DirectDownloadCertificateResponse response) { + try { + Certificate cert = CertificateHelper.buildCertificate(certStr); + if (cert instanceof X509CertImpl) { + X509CertImpl certificate = (X509CertImpl) cert; + response.setVersion(String.valueOf(certificate.getVersion())); + response.setSubject(certificate.getSubjectDN().toString()); + response.setIssuer(certificate.getIssuerDN().toString()); + response.setSerialNum(certificate.getSerialNumberObject().toString()); + response.setValidity(String.format("From: [%s] - To: [%s]", certificate.getNotBefore(), certificate.getNotAfter())); + } + } catch (CertificateException e) { + s_logger.error("Error parsing direct download certificate: " + certStr, e); + } + } + @Override public DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate) { DirectDownloadCertificateResponse response = new DirectDownloadCertificateResponse(); @@ -4564,7 +4584,7 @@ public DirectDownloadCertificateResponse createDirectDownloadCertificateResponse } response.setId(certificate.getUuid()); response.setAlias(certificate.getAlias()); - response.setCertificate(certificate.getCertificate()); + handleCertificateResponse(certificate.getCertificate(), response); response.setHypervisor(certificate.getHypervisorType().name()); response.setObjectName("directdownloadcertificate"); return response; diff --git a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java index 69ac86de8765..6dc96cfee1ff 100644 --- a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java +++ b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java @@ -27,6 +27,7 @@ import java.util.Date; import java.util.TimeZone; +import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.NicSecondaryIpResponse; import org.apache.cloudstack.api.response.UsageRecordResponse; import org.apache.cloudstack.usage.UsageService; @@ -141,4 +142,46 @@ public void setResponseIpAddressTestIpv6() { assertTrue(response.getIpAddr().equals("ipv6")); } + @Test + public void testHandleCertificateResponse() { + String certStr = "-----BEGIN CERTIFICATE-----\n" + + "MIIGLTCCBRWgAwIBAgIQOHZRhOAYLowYNcopBvxCdjANBgkqhkiG9w0BAQsFADCB\n" + + "jzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n" + + "A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQD\n" + + "Ey5TZWN0aWdvIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB\n" + + "MB4XDTIxMDYxNTAwMDAwMFoXDTIyMDcxNjIzNTk1OVowFzEVMBMGA1UEAwwMKi5h\n" + + "cGFjaGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4UoHCmK5\n" + + "XdbyZ++d2BGuX35zZcESvr4K1Hw7ZTbyzMC+uokBKJcng1Hf5ctjUFKCoz7AlWRq\n" + + "JH5U3vU0y515C0aEE+j0lUHlxMGQD2ut+sJ6BZqcTBl5d8ns1TSckEH31DBDN3Fw\n" + + "uMLqEWBOjwt1MMT3Z+kR7ekuheJYbYHbJ2VtnKQd4jHmLly+/p+UqaQ6dIvQxq82\n" + + "ggZIUNWjGKwXS2vKl6O9EDu/QaAX9e059pf3UxAxGtJjeKXWJvt1e96T53+2+kXp\n" + + "j0/PuyT6F0o+grY08tCJnw7kTB4sE2qfALdwSblvyjBDOYtS4Xj5nycMpd+4Qse4\n" + + "2+irNBdZ63pqqQIDAQABo4IC+jCCAvYwHwYDVR0jBBgwFoAUjYxexFStiuF36Zv5\n" + + "mwXhuAGNYeEwHQYDVR0OBBYEFH+9CNXAwWW4+jyizee51r8x4ofHMA4GA1UdDwEB\n" + + "/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF\n" + + "BQcDAjBJBgNVHSAEQjBAMDQGCysGAQQBsjEBAgIHMCUwIwYIKwYBBQUHAgEWF2h0\n" + + "dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgGBmeBDAECATCBhAYIKwYBBQUHAQEEeDB2\n" + + "ME8GCCsGAQUFBzAChkNodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29SU0FE\n" + + "b21haW5WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3J0MCMGCCsGAQUFBzABhhdo\n" + + "dHRwOi8vb2NzcC5zZWN0aWdvLmNvbTAjBgNVHREEHDAaggwqLmFwYWNoZS5vcmeC\n" + + "CmFwYWNoZS5vcmcwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB2AEalVet1+pEg\n" + + "MLWiiWn0830RLEF0vv1JuIWr8vxw/m1HAAABehHLqfgAAAQDAEcwRQIgINH3CquJ\n" + + "zTAprwjdo2cEWkMzpaNoP1SOI4xGl68PF2oCIQC77eD7K6Smx4Fv/z/sTKk21Psb\n" + + "ZhmVq5YoqhwRKuMgVAB2AEHIyrHfIkZKEMahOglCh15OMYsbA+vrS8do8JBilgb2\n" + + "AAABehHLqcEAAAQDAEcwRQIhANh++zJa9AE4U0DsHIFq6bW40b1OfGfH8uUdmjEZ\n" + + "s1jzAiBIRtJeFVmobSnbFKlOr8BGfD2L/hg1rkAgJlKY5oFShgB2ACl5vvCeOTkh\n" + + "8FZzn2Old+W+V32cYAr4+U1dJlwlXceEAAABehHLqZ4AAAQDAEcwRQIhAOZDfvU8\n" + + "Hz80I6Iyj2rv8+yWBVq1XVixI8bMykdCO6ADAiAWj8cJ9g1zxko4dJu8ouJf+Pwl\n" + + "0bbhhuJHhy/f5kiaszANBgkqhkiG9w0BAQsFAAOCAQEAlkdB7FZtVQz39TDNKR4u\n" + + "I8VQsTH5n4Kg+zVc0pptI7HGUWtp5PjBAEsvJ/G/NQXsjVflQaNPRRd7KNZycZL1\n" + + "jls6GdVoWVno6O5aLS7cCnb0tTlb8srhb9vdLZkSoCVCZLVjik5s2TLfpLsBKrTP\n" + + "leVY3n9TBZH+vyKLHt4WHR23Z+74xDsuXunoPGXQVV8ymqTtfohaoM19jP99vjY7\n" + + "DL/289XjMSfyPFqlpU4JDM7lY/kJSKB/C4eQglT8Sgm0h/kj5hdT2uMJBIQZIJVv\n" + + "241fAVUPgrYAESOMm2TVA9r1OzeoUNlKw+e3+vjTR6sfDDp/iRKcEVQX4u9+CxZp\n" + + "9g==\n-----END CERTIFICATE-----"; + DirectDownloadCertificateResponse response = new DirectDownloadCertificateResponse(); + helper.handleCertificateResponse(certStr, response); + assertEquals("3", response.getVersion()); + assertEquals("CN=*.apache.org", response.getSubject()); + } } From 9c864659b3dd1617ddb7f32fa226a21d873f1b97 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Fri, 25 Mar 2022 14:50:28 -0300 Subject: [PATCH 15/22] Address review comments --- ...ListTemplateDirectDownloadCertificatesCmd.java | 2 +- ...evokeTemplateDirectDownloadCertificateCmd.java | 2 +- .../DirectDownloadCertificateHostMapVO.java | 2 +- .../download/DirectDownloadManagerImpl.java | 15 ++++++--------- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java index 22e5d896a310..853490ba83e6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java @@ -43,7 +43,7 @@ import java.util.List; @APICommand(name = ListTemplateDirectDownloadCertificatesCmd.APINAME, - description = "List the uploaded direct download certificates", + description = "List the uploaded certificates for direct download templates", responseObject = DirectDownloadCertificateResponse.class, since = "4.17.0", authorized = {RoleType.Admin}) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java index 465daf6290a4..b92c0748e1c1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java @@ -46,7 +46,7 @@ import java.util.List; @APICommand(name = RevokeTemplateDirectDownloadCertificateCmd.APINAME, - description = "Revoke a certificate from hosts in a zone", + description = "Revoke a direct download certificate from hosts in a zone", responseObject = DirectDownloadCertificateHostStatusResponse.class, since = "4.13", authorized = {RoleType.Admin}) diff --git a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapVO.java b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapVO.java index 491dccc8d80a..7bcfeb65860e 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateHostMapVO.java @@ -25,7 +25,7 @@ @Entity @Table(name = "direct_download_certificate_host_map") -public class DirectDownloadCertificateHostMapVO implements DirectDownloadCertificateHostMap{ +public class DirectDownloadCertificateHostMapVO implements DirectDownloadCertificateHostMap { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index a935c33ef6b2..8805d1c20dbd 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -19,10 +19,7 @@ package org.apache.cloudstack.direct.download; import static com.cloud.storage.Storage.ImageFormat; -import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus.FAILED; -import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus.REVOKED; -import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus.SKIPPED; -import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus.UPLOADED; +import static org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus.CertificateStatus; import java.net.URI; import java.net.URISyntaxException; @@ -505,10 +502,10 @@ public List uploadCertificateToHosts(String certificateCe String msg = "Could not upload certificate " + alias + " on host: " + host.getName() + " (" + host.getUuid() + "): " + result.second(); s_logger.error(msg); failed++; - hostStatus = new HostCertificateStatus(FAILED, host, result.second()); + hostStatus = new HostCertificateStatus(CertificateStatus.FAILED, host, result.second()); } else { success++; - hostStatus = new HostCertificateStatus(UPLOADED, host, ""); + hostStatus = new HostCertificateStatus(CertificateStatus.UPLOADED, host, ""); } results.add(hostStatus); } @@ -650,7 +647,7 @@ public List revokeCertificate(Long certificateId, Long zo if (host != null) { String reason = host.getDataCenterId() != zoneId ? "Host is not in the zone " + zoneId : "Host hypervisor is not KVM"; s_logger.debug("Skipping host " + host.getName() + ": " + reason); - hostStatus = new HostCertificateStatus(SKIPPED, host, reason); + hostStatus = new HostCertificateStatus(CertificateStatus.SKIPPED, host, reason); hostsList.add(hostStatus); } skipped++; @@ -660,12 +657,12 @@ public List revokeCertificate(Long certificateId, Long zo if (!result.first()) { String msg = "Could not revoke certificate from host: " + mappingHostId + ": " + result.second(); s_logger.error(msg); - hostStatus = new HostCertificateStatus(FAILED, host, result.second()); + hostStatus = new HostCertificateStatus(CertificateStatus.FAILED, host, result.second()); failed++; } else { s_logger.info("Certificate " + certificateAlias + " revoked from host " + mappingHostId); map.setRevoked(true); - hostStatus = new HostCertificateStatus(REVOKED, host, null); + hostStatus = new HostCertificateStatus(CertificateStatus.REVOKED, host, null); success++; directDownloadCertificateHostMapDao.update(map.getId(), map); } From 20e50c45b8ff8e7c6e07d296462731db85f267e2 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Thu, 31 Mar 2022 13:02:49 -0300 Subject: [PATCH 16/22] Refactor revoke cert test --- test/integration/smoke/test_direct_download.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/integration/smoke/test_direct_download.py b/test/integration/smoke/test_direct_download.py index b894f08934b2..e6ac2431af41 100644 --- a/test/integration/smoke/test_direct_download.py +++ b/test/integration/smoke/test_direct_download.py @@ -18,7 +18,7 @@ """ # Import Local Modules from marvin.cloudstackTestCase import cloudstackTestCase -from marvin.lib.utils import (cleanup_resources) +from marvin.lib.utils import (cleanup_resources, validateList) from marvin.lib.base import (ServiceOffering, NetworkOffering, Network, @@ -132,13 +132,14 @@ def test_02_upload_direct_download_certificates(self): cmd.zoneid = self.zone.id try: - self.apiclient.uploadTemplateDirectDownloadCertificate(cmd) + certs = self.apiclient.uploadTemplateDirectDownloadCertificate(cmd) + validateList(certs) + cert = certs[0] except Exception as e: self.fail("Valid certificate must be uploaded") revokecmd = revokeTemplateDirectDownloadCertificate.revokeTemplateDirectDownloadCertificateCmd() - revokecmd.hypervisor = self.hypervisor - revokecmd.name = cmd.name + revokecmd.id = cert.id revokecmd.zoneid = self.zone.id try: @@ -149,6 +150,7 @@ def test_02_upload_direct_download_certificates(self): return + class TestDirectDownloadTemplates(cloudstackTestCase): @classmethod From 1e840d204ced5cdaf3ba1d50723dec736ace1d2c Mon Sep 17 00:00:00 2001 From: nvazquez Date: Sun, 3 Apr 2022 00:42:54 -0300 Subject: [PATCH 17/22] Fix marvin test --- test/integration/smoke/test_direct_download.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/integration/smoke/test_direct_download.py b/test/integration/smoke/test_direct_download.py index e6ac2431af41..6570bb9f0b3c 100644 --- a/test/integration/smoke/test_direct_download.py +++ b/test/integration/smoke/test_direct_download.py @@ -28,7 +28,8 @@ from marvin.lib.common import (get_pod, get_zone) from nose.plugins.attrib import attr -from marvin.cloudstackAPI import (uploadTemplateDirectDownloadCertificate, revokeTemplateDirectDownloadCertificate) +from marvin.cloudstackAPI import (uploadTemplateDirectDownloadCertificate, revokeTemplateDirectDownloadCertificate, + listTemplateDirectDownloadCertificates) from marvin.lib.decoratorGenerators import skipTestIf import uuid @@ -132,12 +133,15 @@ def test_02_upload_direct_download_certificates(self): cmd.zoneid = self.zone.id try: - certs = self.apiclient.uploadTemplateDirectDownloadCertificate(cmd) - validateList(certs) - cert = certs[0] + self.apiclient.uploadTemplateDirectDownloadCertificate(cmd) except Exception as e: self.fail("Valid certificate must be uploaded") + cmd = listTemplateDirectDownloadCertificates.listTemplateDirectDownloadCertificatesCmd() + certs = self.apiclient.listTemplateDirectDownloadCertificates(cmd) + validateList(certs) + cert = certs[0] + revokecmd = revokeTemplateDirectDownloadCertificate.revokeTemplateDirectDownloadCertificateCmd() revokecmd.id = cert.id revokecmd.zoneid = self.zone.id From 76c1ca388a9792b0373dc68920b107ac3af9fd6e Mon Sep 17 00:00:00 2001 From: nvazquez Date: Mon, 4 Apr 2022 15:26:36 -0300 Subject: [PATCH 18/22] Address review comments --- .../cloudstack/api/ResponseGenerator.java | 8 ++- ...TemplateDirectDownloadCertificatesCmd.java | 4 +- ...nTemplateDirectDownloadCertificateCmd.java | 15 ++--- ...eTemplateDirectDownloadCertificateCmd.java | 2 +- ...dTemplateDirectDownloadCertificateCmd.java | 2 +- ...ectDownloadCertificateHostMapResponse.java | 61 ------------------- .../DirectDownloadCertificateResponse.java | 6 +- .../java/com/cloud/api/ApiResponseHelper.java | 34 +++++++---- .../cloud/server/ManagementServerImpl.java | 2 + 9 files changed, 41 insertions(+), 93 deletions(-) delete mode 100644 api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostMapResponse.java diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index b7c564e0eb4c..6e149b60edf6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -23,7 +23,7 @@ import java.util.Set; import com.cloud.server.ResourceIcon; -import org.apache.cloudstack.api.response.DirectDownloadCertificateHostMapResponse; +import com.cloud.utils.Pair; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; @@ -499,7 +499,9 @@ List createTemplateResponses(ResponseView view, VirtualMachine DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate); - List createDirectDownloadCertificateHostMapResponse(List hostMappings); + List createDirectDownloadCertificateHostMapResponse(List hostMappings); - DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus status, String objectName); + DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus status); + + DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId, Long hostId, Pair result); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java index 853490ba83e6..21480c1ef53d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ListTemplateDirectDownloadCertificatesCmd.java @@ -28,7 +28,7 @@ import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.DirectDownloadCertificateHostMapResponse; +import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ZoneResponse; @@ -81,7 +81,7 @@ private void createResponse(final List certificates) DirectDownloadCertificateResponse certificateResponse = _responseGenerator.createDirectDownloadCertificateResponse(certificate); if (isListHosts()) { List hostMappings = directDownloadManager.getCertificateHostsMapping(certificate.getId()); - List hostMapResponses = _responseGenerator.createDirectDownloadCertificateHostMapResponse(hostMappings); + List hostMapResponses = _responseGenerator.createDirectDownloadCertificateHostMapResponse(hostMappings); certificateResponse.setHostsMap(hostMapResponses); } responses.add(certificateResponse); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java index f733affddded..715645ea8919 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java @@ -27,13 +27,12 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.direct.download.DirectDownloadManager; @@ -41,7 +40,7 @@ @APICommand(name = ProvisionTemplateDirectDownloadCertificateCmd.APINAME, description = "Provisions a host with a direct download certificate", - responseObject = SuccessResponse.class, + responseObject = DirectDownloadCertificateHostStatusResponse.class, since = "4.17.0", authorized = {RoleType.Admin}) public class ProvisionTemplateDirectDownloadCertificateCmd extends BaseCmd { @@ -62,13 +61,9 @@ public class ProvisionTemplateDirectDownloadCertificateCmd extends BaseCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { Pair result = directDownloadManager.provisionCertificate(id, hostId); - if (result.first()) { - SuccessResponse response = new SuccessResponse(getCommandName()); - response.setDisplayText(result.second()); - setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to provision certificate: " + result.second()); - } + DirectDownloadCertificateHostStatusResponse response = _responseGenerator.createDirectDownloadCertificateProvisionResponse(id, hostId, result); + response.setResponseName(getCommandName()); + setResponseObject(response); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java index b92c0748e1c1..7640e90cccb4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java @@ -79,7 +79,7 @@ private void createResponse(final List hostsRevokeStatusL continue; } DirectDownloadCertificateHostStatusResponse revokeResponse = - _responseGenerator.createDirectDownloadCertificateHostStatusResponse(status, "revoketemplatedirectdownloadcertificate"); + _responseGenerator.createDirectDownloadCertificateHostStatusResponse(status); responses.add(revokeResponse); } response.setResponses(responses); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java index d70327612498..254b2d4173c7 100755 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java @@ -77,7 +77,7 @@ private void createResponse(final List hostStatusList) { continue; } DirectDownloadCertificateHostStatusResponse revokeResponse = - _responseGenerator.createDirectDownloadCertificateHostStatusResponse(status, "uploadtemplatedirectdownloadcertificate"); + _responseGenerator.createDirectDownloadCertificateHostStatusResponse(status); responses.add(revokeResponse); } response.setResponses(responses); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostMapResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostMapResponse.java deleted file mode 100644 index fbeed26dfe33..000000000000 --- a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateHostMapResponse.java +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package org.apache.cloudstack.api.response; - -import com.cloud.serializer.Param; -import com.google.gson.annotations.SerializedName; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseResponse; - -public class DirectDownloadCertificateHostMapResponse extends BaseResponse { - - @SerializedName(ApiConstants.HOST_ID) - @Param(description = "the ID of the host") - private String hostId; - - @SerializedName(ApiConstants.HOST_NAME) - @Param(description = "the name of the host") - private String hostName; - - @SerializedName(ApiConstants.REVOKED) - @Param(description = "indicates if the certificate has been revoked from the host") - private Boolean revoked; - - public String getHostId() { - return hostId; - } - - public void setHostId(String hostId) { - this.hostId = hostId; - } - - public String getHostName() { - return hostName; - } - - public void setHostName(String hostName) { - this.hostName = hostName; - } - - public Boolean getRevoked() { - return revoked; - } - - public void setRevoked(Boolean revoked) { - this.revoked = revoked; - } -} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java index fdf21d8fb725..f04cba812ba5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DirectDownloadCertificateResponse.java @@ -70,7 +70,7 @@ public class DirectDownloadCertificateResponse extends BaseResponse { @SerializedName(ApiConstants.HOSTS_MAP) @Param(description = "the hosts where the certificate is uploaded to", responseObject = HostResponse.class) - private List hostsMap; + private List hostsMap; public String getId() { return id; @@ -112,11 +112,11 @@ public void setZoneName(String zoneName) { this.zoneName = zoneName; } - public List getHostsMap() { + public List getHostsMap() { return hostsMap; } - public void setHostsMap(List hosts) { + public void setHostsMap(List hosts) { this.hostsMap = hosts; } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index f70b48240fe6..7d89ccc0e9cc 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -71,7 +71,6 @@ import org.apache.cloudstack.api.response.CreateCmdResponse; import org.apache.cloudstack.api.response.CreateSSHKeyPairResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; -import org.apache.cloudstack.api.response.DirectDownloadCertificateHostMapResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.DomainRouterResponse; @@ -4591,37 +4590,48 @@ public DirectDownloadCertificateResponse createDirectDownloadCertificateResponse } @Override - public List createDirectDownloadCertificateHostMapResponse(List hostMappings) { + public List createDirectDownloadCertificateHostMapResponse(List hostMappings) { if (CollectionUtils.isEmpty(hostMappings)) { return new ArrayList<>(); } - List responses = new ArrayList<>(hostMappings.size()); + List responses = new ArrayList<>(hostMappings.size()); for (DirectDownloadCertificateHostMap map : hostMappings) { - DirectDownloadCertificateHostMapResponse response = new DirectDownloadCertificateHostMapResponse(); + DirectDownloadCertificateHostStatusResponse response = new DirectDownloadCertificateHostStatusResponse(); HostVO host = ApiDBUtils.findHostById(map.getHostId()); if (host != null) { response.setHostId(host.getUuid()); response.setHostName(host.getName()); } - response.setRevoked(map.isRevoked()); - response.setObjectName("directdownloadcertificatehostmap"); + response.setStatus(map.isRevoked() ? CertificateStatus.REVOKED.name() : CertificateStatus.UPLOADED.name()); + response.setObjectName("directdownloadcertificatehoststatus"); responses.add(response); } return responses; } - @Override - public DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus hostStatus, String objectName) { + private DirectDownloadCertificateHostStatusResponse getDirectDownloadHostStatusResponseInternal(Host host, CertificateStatus status, String details) { DirectDownloadCertificateHostStatusResponse response = new DirectDownloadCertificateHostStatusResponse(); - Host host = hostStatus.getHost(); if (host != null) { response.setHostId(host.getUuid()); response.setHostName(host.getName()); } - CertificateStatus status = hostStatus.getStatus(); response.setStatus(status.name()); - response.setDetails(hostStatus.getDetails()); - response.setObjectName(objectName); + response.setDetails(details); + response.setObjectName("directdownloadcertificatehoststatus"); return response; } + + @Override + public DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus hostStatus) { + Host host = hostStatus.getHost(); + CertificateStatus status = hostStatus.getStatus(); + return getDirectDownloadHostStatusResponseInternal(host, status, hostStatus.getDetails()); + } + + @Override + public DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId, Long hostId, Pair result) { + HostVO host = ApiDBUtils.findHostById(hostId); + CertificateStatus status = result != null && result.first() ? CertificateStatus.UPLOADED : CertificateStatus.FAILED; + return getDirectDownloadHostStatusResponseInternal(host, status, result != null ? result.second() : "provision certificate failure"); + } } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 5af3d43728f7..9fad4782c402 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -75,6 +75,7 @@ import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd; import org.apache.cloudstack.api.command.admin.direct.download.ListTemplateDirectDownloadCertificatesCmd; +import org.apache.cloudstack.api.command.admin.direct.download.ProvisionTemplateDirectDownloadCertificateCmd; import org.apache.cloudstack.api.command.admin.direct.download.RevokeTemplateDirectDownloadCertificateCmd; import org.apache.cloudstack.api.command.admin.direct.download.UploadTemplateDirectDownloadCertificateCmd; import org.apache.cloudstack.api.command.admin.domain.CreateDomainCmd; @@ -3540,6 +3541,7 @@ public List> getCommands() { cmdList.add(UploadTemplateDirectDownloadCertificateCmd.class); cmdList.add(RevokeTemplateDirectDownloadCertificateCmd.class); cmdList.add(ListTemplateDirectDownloadCertificatesCmd.class); + cmdList.add(ProvisionTemplateDirectDownloadCertificateCmd.class); cmdList.add(ListMgmtsCmd.class); cmdList.add(GetUploadParamsForIsoCmd.class); cmdList.add(GetRouterHealthCheckResultsCmd.class); From f6c5970db44e3a94af0ae516899f0c663fe0c675 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Tue, 5 Apr 2022 13:57:36 -0300 Subject: [PATCH 19/22] Fix issues --- ...nTemplateDirectDownloadCertificateCmd.java | 4 ++-- ...eTemplateDirectDownloadCertificateCmd.java | 19 +++++++++++++++---- ...dTemplateDirectDownloadCertificateCmd.java | 3 +-- .../download/DirectDownloadManager.java | 3 ++- .../download/DirectDownloadManagerImpl.java | 14 ++++++++++++-- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java index 715645ea8919..e68577c4e29b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java @@ -51,11 +51,11 @@ public class ProvisionTemplateDirectDownloadCertificateCmd extends BaseCmd { DirectDownloadManager directDownloadManager; @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class, - description = "the id of the direct download certificate to provision") + description = "the id of the direct download certificate to provision", required = true) private Long id; @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, - description = "the host to provision the certificate") + description = "the host to provision the certificate", required = true) private Long hostId; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java index 7640e90cccb4..4320c32cd91e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java @@ -28,7 +28,6 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; @@ -50,7 +49,7 @@ responseObject = DirectDownloadCertificateHostStatusResponse.class, since = "4.13", authorized = {RoleType.Admin}) -public class RevokeTemplateDirectDownloadCertificateCmd extends BaseListCmd { +public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd { @Inject DirectDownloadManager directDownloadManager; @@ -63,8 +62,16 @@ public class RevokeTemplateDirectDownloadCertificateCmd extends BaseListCmd { description = "id of the certificate") private Long certificateId; + @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, + description = "(optional) alias of the SSL certificate") + private String certificateAlias; + + @Parameter(name = ApiConstants.HYPERVISOR, type = BaseCmd.CommandType.STRING, + description = "(optional) hypervisor type") + private String hypervisor; + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - description = "zone to revoke certificate", required = true) + description = "(optional) zone to revoke certificate", required = true) private Long zoneId; @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, @@ -89,8 +96,12 @@ private void createResponse(final List hostsRevokeStatusL @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + if (!hypervisor.equalsIgnoreCase("kvm")) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Currently supporting KVM hosts only"); + } try { - List hostsResult = directDownloadManager.revokeCertificate(certificateId, zoneId, hostId); + List hostsResult = directDownloadManager.revokeCertificate(certificateId, + certificateAlias, hypervisor, zoneId, hostId); createResponse(hostsResult); } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed revoking certificate: " + e.getMessage()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java index 254b2d4173c7..0668c265ad78 100755 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java @@ -20,7 +20,6 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.ApiErrorCode; @@ -42,7 +41,7 @@ responseObject = DirectDownloadCertificateHostStatusResponse.class, since = "4.11.0", authorized = {RoleType.Admin}) -public class UploadTemplateDirectDownloadCertificateCmd extends BaseListCmd { +public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd { @Inject DirectDownloadManager directDownloadManager; diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java index 4e13f0ee8e4e..d88ff5701243 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java @@ -89,7 +89,8 @@ public String getDetails() { /** * Revoke direct download certificate from the hosts in the zone or a specific host */ - List revokeCertificate(Long certificateId, Long zoneId, Long hostId); + List revokeCertificate(Long certificateId, String certificateAlias, String hypervisor, + Long zoneId, Long hostId); List listDirectDownloadCertificates(Long certificateId, Long zoneId); diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index 8805d1c20dbd..6b0bbc877fcc 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -70,6 +70,7 @@ import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -618,11 +619,20 @@ private List getCertificateHostMappings(Dire } @Override - public List revokeCertificate(Long certificateId, Long zoneId, Long hostId) { - DirectDownloadCertificateVO certificateVO = directDownloadCertificateDao.findById(certificateId); + public List revokeCertificate(Long certificateId, String alias, String hypervisor, Long zoneId, Long hostId) { + DirectDownloadCertificateVO certificateVO; + if (certificateId != null) { + certificateVO = directDownloadCertificateDao.findById(certificateId); + } else if (StringUtils.isNotBlank(alias)) { + certificateVO = directDownloadCertificateDao.findByAlias(alias, HypervisorType.getType(hypervisor), zoneId); + } else { + throw new CloudRuntimeException("Please provide a certificate ID or certificate alias"); + } + if (certificateVO == null) { throw new CloudRuntimeException("Certificate with ID " + certificateId + " does not exist"); } + String certificateAlias = certificateVO.getAlias(); if (!certificateVO.getZoneId().equals(zoneId)) { throw new CloudRuntimeException("The certificate with alias " + certificateAlias + " was uploaded " + From 2713b102b77e80a3ec6124a7526e575f42f9bd25 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Tue, 5 Apr 2022 23:25:17 -0300 Subject: [PATCH 20/22] Improvements --- ...nTemplateDirectDownloadCertificateCmd.java | 2 +- ...eTemplateDirectDownloadCertificateCmd.java | 24 +++++++++---- .../download/DirectDownloadManager.java | 5 +-- .../download/DirectDownloadManagerImpl.java | 36 ++++++++++--------- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java index e68577c4e29b..b7574b438d0d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/ProvisionTemplateDirectDownloadCertificateCmd.java @@ -45,7 +45,7 @@ authorized = {RoleType.Admin}) public class ProvisionTemplateDirectDownloadCertificateCmd extends BaseCmd { - public static final String APINAME = "provisionTemplateDirectDownloadCertificates"; + public static final String APINAME = "provisionTemplateDirectDownloadCertificate"; @Inject DirectDownloadManager directDownloadManager; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java index 4320c32cd91e..507d73f134ec 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/RevokeTemplateDirectDownloadCertificateCmd.java @@ -36,8 +36,11 @@ import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadManager; import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import javax.inject.Inject; @@ -57,7 +60,7 @@ public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd { private static final Logger LOG = Logger.getLogger(RevokeTemplateDirectDownloadCertificateCmd.class); public static final String APINAME = "revokeTemplateDirectDownloadCertificate"; - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class, description = "id of the certificate") private Long certificateId; @@ -94,14 +97,23 @@ private void createResponse(final List hostsRevokeStatusL setResponseObject(response); } - @Override - public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { - if (!hypervisor.equalsIgnoreCase("kvm")) { + private void validateParameters() { + if (ObjectUtils.allNull(certificateId, certificateAlias, hypervisor) || + certificateId == null && !ObjectUtils.allNotNull(certificateAlias, hypervisor)) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Please specify the hypervisor and the" + + "certificate name to revoke or the certificate ID"); + } + if (StringUtils.isNotBlank(hypervisor) && !hypervisor.equalsIgnoreCase("kvm")) { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Currently supporting KVM hosts only"); } + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + validateParameters(); try { - List hostsResult = directDownloadManager.revokeCertificate(certificateId, - certificateAlias, hypervisor, zoneId, hostId); + DirectDownloadCertificate certificate = directDownloadManager.findDirectDownloadCertificateByIdOrHypervisorAndAlias(certificateId, certificateAlias, hypervisor, zoneId); + List hostsResult = directDownloadManager.revokeCertificate(certificate, zoneId, hostId); createResponse(hostsResult); } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed revoking certificate: " + e.getMessage()); diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java index d88ff5701243..c735e67eaad8 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java @@ -86,11 +86,12 @@ public String getDetails() { } } + DirectDownloadCertificate findDirectDownloadCertificateByIdOrHypervisorAndAlias(Long id, String alias, String hypervisor, Long zoneId); + /** * Revoke direct download certificate from the hosts in the zone or a specific host */ - List revokeCertificate(Long certificateId, String certificateAlias, String hypervisor, - Long zoneId, Long hostId); + List revokeCertificate(DirectDownloadCertificate certificate, Long zoneId, Long hostId); List listDirectDownloadCertificates(Long certificateId, Long zoneId); diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index 6b0bbc877fcc..a8ee15180759 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -599,18 +599,18 @@ public boolean syncCertificatesToHost(long hostId, long zoneId) { return syncCertificatesResult; } - private List getCertificateHostMappings(DirectDownloadCertificateVO certificateVO, Long hostId) { + private List getCertificateHostMappings(DirectDownloadCertificate certificate, Long hostId) { List maps; if (hostId == null) { - maps = directDownloadCertificateHostMapDao.listByCertificateIdAndRevoked(certificateVO.getId(), false); + maps = directDownloadCertificateHostMapDao.listByCertificateIdAndRevoked(certificate.getId(), false); } else { - DirectDownloadCertificateHostMapVO hostMap = directDownloadCertificateHostMapDao.findByCertificateAndHost(certificateVO.getId(), hostId); + DirectDownloadCertificateHostMapVO hostMap = directDownloadCertificateHostMapDao.findByCertificateAndHost(certificate.getId(), hostId); if (hostMap == null) { - String msg = "Certificate " + certificateVO.getAlias() + " cannot be revoked from host " + hostId + " as it is not available on the host"; + String msg = "Certificate " + certificate.getAlias() + " cannot be revoked from host " + hostId + " as it is not available on the host"; s_logger.error(msg); throw new CloudRuntimeException(msg); } else if (hostMap.isRevoked()) { - s_logger.debug("Certificate " + certificateVO.getAlias() + " was already revoked from host " + hostId + " skipping it"); + s_logger.debug("Certificate " + certificate.getAlias() + " was already revoked from host " + hostId + " skipping it"); return new LinkedList<>(); } maps = Collections.singletonList(hostMap); @@ -619,28 +619,32 @@ private List getCertificateHostMappings(Dire } @Override - public List revokeCertificate(Long certificateId, String alias, String hypervisor, Long zoneId, Long hostId) { + public DirectDownloadCertificate findDirectDownloadCertificateByIdOrHypervisorAndAlias(Long id, String alias, String hypervisor, Long zoneId) { DirectDownloadCertificateVO certificateVO; - if (certificateId != null) { - certificateVO = directDownloadCertificateDao.findById(certificateId); - } else if (StringUtils.isNotBlank(alias)) { + if (id != null) { + certificateVO = directDownloadCertificateDao.findById(id); + } else if (StringUtils.isNotBlank(alias) && StringUtils.isNotBlank(hypervisor)) { certificateVO = directDownloadCertificateDao.findByAlias(alias, HypervisorType.getType(hypervisor), zoneId); } else { - throw new CloudRuntimeException("Please provide a certificate ID or certificate alias"); + throw new CloudRuntimeException("Please provide a hypervisor and certificate alias or certificate ID"); } - if (certificateVO == null) { - throw new CloudRuntimeException("Certificate with ID " + certificateId + " does not exist"); + throw new CloudRuntimeException("Could not find certificate " + + (id != null ? "with ID " + id : "with alias " + alias + " and hypervisor " + hypervisor)); } + return certificateVO; + } - String certificateAlias = certificateVO.getAlias(); - if (!certificateVO.getZoneId().equals(zoneId)) { + @Override + public List revokeCertificate(DirectDownloadCertificate certificate, Long zoneId, Long hostId) { + String certificateAlias = certificate.getAlias(); + if (!certificate.getZoneId().equals(zoneId)) { throw new CloudRuntimeException("The certificate with alias " + certificateAlias + " was uploaded " + - " to the zone with ID=" + certificateVO.getZoneId() + " instead of the zone with ID=" + zoneId); + " to the zone with ID=" + certificate.getZoneId() + " instead of the zone with ID=" + zoneId); } List hostsList = new ArrayList<>(); - List maps = getCertificateHostMappings(certificateVO, hostId); + List maps = getCertificateHostMappings(certificate, hostId); if (CollectionUtils.isEmpty(maps)) { return hostsList; } From fa872be40a25cf1e0a5f63ab2ac0df3c22545a22 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Mon, 11 Apr 2022 08:05:45 -0300 Subject: [PATCH 21/22] Refactor upload template API response --- ...dTemplateDirectDownloadCertificateCmd.java | 31 +++++++++++++------ .../download/DirectDownloadManager.java | 3 +- .../download/DirectDownloadManagerImpl.java | 7 +++-- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java index 0668c265ad78..5dac08a6584d 100755 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.command.admin.direct.download; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -23,11 +25,12 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadManager; import org.apache.cloudstack.direct.download.DirectDownloadManager.HostCertificateStatus; import org.apache.log4j.Logger; @@ -38,7 +41,7 @@ @APICommand(name = UploadTemplateDirectDownloadCertificateCmd.APINAME, description = "Upload a certificate for HTTPS direct template download on KVM hosts", - responseObject = DirectDownloadCertificateHostStatusResponse.class, + responseObject = DirectDownloadCertificateResponse.class, since = "4.11.0", authorized = {RoleType.Admin}) public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd { @@ -68,19 +71,24 @@ public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd { description = "(optional) the host ID to upload certificate") private Long hostId; - private void createResponse(final List hostStatusList) { - final ListResponse response = new ListResponse<>(); - final List responses = new ArrayList<>(); + private void createResponse(DirectDownloadCertificate certificate, final List hostStatusList) { + final DirectDownloadCertificateResponse response = new DirectDownloadCertificateResponse(); + final List hostMapsResponse = new ArrayList<>(); + if (certificate == null) { + throw new CloudRuntimeException("Unable to upload certificate"); + } + DirectDownloadCertificateResponse certificateResponse = _responseGenerator.createDirectDownloadCertificateResponse(certificate); for (final HostCertificateStatus status : hostStatusList) { if (status == null) { continue; } - DirectDownloadCertificateHostStatusResponse revokeResponse = + DirectDownloadCertificateHostStatusResponse uploadResponse = _responseGenerator.createDirectDownloadCertificateHostStatusResponse(status); - responses.add(revokeResponse); + hostMapsResponse.add(uploadResponse); } - response.setResponses(responses); + certificateResponse.setHostsMap(hostMapsResponse); response.setResponseName(getCommandName()); + response.setObjectName("uploadtemplatedirectdownloadcertificate"); setResponseObject(response); } @@ -92,8 +100,11 @@ public void execute() { try { LOG.debug("Uploading certificate " + name + " to agents for Direct Download"); - List hostStatus = directDownloadManager.uploadCertificateToHosts(certificate, name, hypervisor, zoneId, hostId); - createResponse(hostStatus); + Pair> uploadStatus = + directDownloadManager.uploadCertificateToHosts(certificate, name, hypervisor, zoneId, hostId); + DirectDownloadCertificate certificate = uploadStatus.first(); + List hostStatus = uploadStatus.second(); + createResponse(certificate, hostStatus); } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } diff --git a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java index c735e67eaad8..8a74965cecab 100644 --- a/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java +++ b/api/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManager.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.direct.download; import com.cloud.host.Host; +import com.cloud.utils.Pair; import org.apache.cloudstack.framework.agent.direct.download.DirectDownloadService; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; @@ -101,5 +102,5 @@ public String getDetails() { * Upload client certificate to each running host * @return */ - List uploadCertificateToHosts(String certificateCer, String certificateName, String hypervisor, Long zoneId, Long hostId); + Pair> uploadCertificateToHosts(String certificateCer, String certificateName, String hypervisor, Long zoneId, Long hostId); } diff --git a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java index a8ee15180759..242dc865b932 100644 --- a/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadManagerImpl.java @@ -453,7 +453,8 @@ protected void certificateSanity(String certificatePem) { } @Override - public List uploadCertificateToHosts(String certificateCer, String alias, String hypervisor, Long zoneId, Long hostId) { + public Pair> uploadCertificateToHosts( + String certificateCer, String alias, String hypervisor, Long zoneId, Long hostId) { if (alias != null && (alias.equalsIgnoreCase("cloud") || alias.startsWith("cloudca"))) { throw new CloudRuntimeException("Please provide a different alias name for the certificate"); } @@ -484,7 +485,7 @@ public List uploadCertificateToHosts(String certificateCe certificateVO = directDownloadCertificateDao.findByAlias(alias, hypervisorType, zoneId); if (certificateVO == null) { s_logger.info("Certificate must be uploaded on zone " + zoneId); - return new ArrayList<>(); + return new Pair<>(certificateVO, new ArrayList<>()); } } @@ -512,7 +513,7 @@ public List uploadCertificateToHosts(String certificateCe } } s_logger.info("Certificate was successfully uploaded to " + success + " hosts, " + failed + " failed"); - return results; + return new Pair<>(certificateVO, results); } private Pair setupCertificateOnHost(DirectDownloadCertificate certificate, long hostId) { From eb9458db9d548d4c13327eadbc5eefd36ad6c5d5 Mon Sep 17 00:00:00 2001 From: nvazquez Date: Mon, 11 Apr 2022 08:35:20 -0300 Subject: [PATCH 22/22] Fix response --- .../download/UploadTemplateDirectDownloadCertificateCmd.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java index 5dac08a6584d..e403f11baac7 100755 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java @@ -72,12 +72,11 @@ public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd { private Long hostId; private void createResponse(DirectDownloadCertificate certificate, final List hostStatusList) { - final DirectDownloadCertificateResponse response = new DirectDownloadCertificateResponse(); final List hostMapsResponse = new ArrayList<>(); if (certificate == null) { throw new CloudRuntimeException("Unable to upload certificate"); } - DirectDownloadCertificateResponse certificateResponse = _responseGenerator.createDirectDownloadCertificateResponse(certificate); + DirectDownloadCertificateResponse response = _responseGenerator.createDirectDownloadCertificateResponse(certificate); for (final HostCertificateStatus status : hostStatusList) { if (status == null) { continue; @@ -86,7 +85,7 @@ private void createResponse(DirectDownloadCertificate certificate, final List