mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Direct download certificates additions and improvements (#6104)
* Add direct download certificates listing * Restore class to original project * Small refactor * Register API * Apply suggestions from code review Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com> * Refactor after review * Fix checkstyle * Add hosts mapping to API response * Improvements on revoke certificate * Refactor revoke certificate API * Fix condition * Filter only certificates not revoked for revokeCertificate API * Improve upload certificate and add provision certificate API * Improve certificate response output * Address review comments * Refactor revoke cert test * Fix marvin test * Address review comments * Fix issues * Improvements * Refactor upload template API response * Fix response Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>
This commit is contained in:
parent
177f04839c
commit
5435b0abfe
@ -26,6 +26,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";
|
||||
@ -58,6 +59,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";
|
||||
@ -188,6 +193,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 +243,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 +329,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";
|
||||
|
||||
@ -23,10 +23,16 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.cloud.server.ResourceIcon;
|
||||
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;
|
||||
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;
|
||||
@ -491,4 +497,11 @@ public interface ResponseGenerator {
|
||||
|
||||
ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon);
|
||||
|
||||
DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate);
|
||||
|
||||
List<DirectDownloadCertificateHostStatusResponse> createDirectDownloadCertificateHostMapResponse(List<DirectDownloadCertificateHostMap> hostMappings);
|
||||
|
||||
DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus status);
|
||||
|
||||
DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId, Long hostId, Pair<Boolean, String> result);
|
||||
}
|
||||
|
||||
@ -0,0 +1,110 @@
|
||||
// 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.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.DirectDownloadCertificateHostStatusResponse;
|
||||
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;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@APICommand(name = ListTemplateDirectDownloadCertificatesCmd.APINAME,
|
||||
description = "List the uploaded certificates for direct download templates",
|
||||
responseObject = DirectDownloadCertificateResponse.class,
|
||||
since = "4.17.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class ListTemplateDirectDownloadCertificatesCmd 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;
|
||||
|
||||
@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<DirectDownloadCertificate> certificates) {
|
||||
final ListResponse<DirectDownloadCertificateResponse> response = new ListResponse<>();
|
||||
final List<DirectDownloadCertificateResponse> responses = new ArrayList<>();
|
||||
for (final DirectDownloadCertificate certificate : certificates) {
|
||||
if (certificate == null) {
|
||||
continue;
|
||||
}
|
||||
DirectDownloadCertificateResponse certificateResponse = _responseGenerator.createDirectDownloadCertificateResponse(certificate);
|
||||
if (isListHosts()) {
|
||||
List<DirectDownloadCertificateHostMap> hostMappings = directDownloadManager.getCertificateHostsMapping(certificate.getId());
|
||||
List<DirectDownloadCertificateHostStatusResponse> hostMapResponses = _responseGenerator.createDirectDownloadCertificateHostMapResponse(hostMappings);
|
||||
certificateResponse.setHostsMap(hostMapResponses);
|
||||
}
|
||||
responses.add(certificateResponse);
|
||||
}
|
||||
response.setResponses(responses);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
|
||||
ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
List<DirectDownloadCertificate> 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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
//
|
||||
// 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.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.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 = DirectDownloadCertificateHostStatusResponse.class,
|
||||
since = "4.17.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class ProvisionTemplateDirectDownloadCertificateCmd extends BaseCmd {
|
||||
|
||||
public static final String APINAME = "provisionTemplateDirectDownloadCertificate";
|
||||
|
||||
@Inject
|
||||
DirectDownloadManager directDownloadManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DirectDownloadCertificateResponse.class,
|
||||
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", required = true)
|
||||
private Long hostId;
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
Pair<Boolean, String> result = directDownloadManager.provisionCertificate(id, hostId);
|
||||
DirectDownloadCertificateHostStatusResponse response = _responseGenerator.createDirectDownloadCertificateProvisionResponse(id, hostId, result);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
}
|
||||
@ -30,20 +30,26 @@ 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.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.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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 direct download certificate from hosts in a zone",
|
||||
responseObject = DirectDownloadCertificateHostStatusResponse.class,
|
||||
since = "4.13",
|
||||
authorized = {RoleType.Admin})
|
||||
public class RevokeTemplateDirectDownloadCertificateCmd extends BaseCmd {
|
||||
@ -54,35 +60,63 @@ 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")
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID,
|
||||
entityType = DirectDownloadCertificateResponse.class,
|
||||
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, required = true,
|
||||
description = "hypervisor type")
|
||||
@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,
|
||||
description = "(optional) the host ID to revoke certificate")
|
||||
private Long hostId;
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
if (!hypervisor.equalsIgnoreCase("kvm")) {
|
||||
private void createResponse(final List<HostCertificateStatus> hostsRevokeStatusList) {
|
||||
final ListResponse<DirectDownloadCertificateHostStatusResponse> response = new ListResponse<>();
|
||||
final List<DirectDownloadCertificateHostStatusResponse> responses = new ArrayList<>();
|
||||
for (final HostCertificateStatus status : hostsRevokeStatusList) {
|
||||
if (status == null) {
|
||||
continue;
|
||||
}
|
||||
DirectDownloadCertificateHostStatusResponse revokeResponse =
|
||||
_responseGenerator.createDirectDownloadCertificateHostStatusResponse(status);
|
||||
responses.add(revokeResponse);
|
||||
}
|
||||
response.setResponses(responses);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
validateParameters();
|
||||
try {
|
||||
LOG.debug("Revoking certificate " + certificateAlias + " from " + hypervisor + " hosts");
|
||||
boolean result = directDownloadManager.revokeCertificateAlias(certificateAlias, hypervisor, zoneId, hostId);
|
||||
response.setSuccess(result);
|
||||
setResponseObject(response);
|
||||
DirectDownloadCertificate certificate = directDownloadManager.findDirectDownloadCertificateByIdOrHypervisorAndAlias(certificateId, certificateAlias, hypervisor, zoneId);
|
||||
List<HostCertificateStatus> hostsResult = directDownloadManager.revokeCertificate(certificate, 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,20 +25,23 @@ import org.apache.cloudstack.api.BaseCmd;
|
||||
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.SuccessResponse;
|
||||
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;
|
||||
|
||||
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 = DirectDownloadCertificateResponse.class,
|
||||
since = "4.11.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd {
|
||||
@ -63,9 +68,29 @@ 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(DirectDownloadCertificate certificate, final List<HostCertificateStatus> hostStatusList) {
|
||||
final List<DirectDownloadCertificateHostStatusResponse> hostMapsResponse = new ArrayList<>();
|
||||
if (certificate == null) {
|
||||
throw new CloudRuntimeException("Unable to upload certificate");
|
||||
}
|
||||
DirectDownloadCertificateResponse response = _responseGenerator.createDirectDownloadCertificateResponse(certificate);
|
||||
for (final HostCertificateStatus status : hostStatusList) {
|
||||
if (status == null) {
|
||||
continue;
|
||||
}
|
||||
DirectDownloadCertificateHostStatusResponse uploadResponse =
|
||||
_responseGenerator.createDirectDownloadCertificateHostStatusResponse(status);
|
||||
hostMapsResponse.add(uploadResponse);
|
||||
}
|
||||
response.setHostsMap(hostMapsResponse);
|
||||
response.setResponseName(getCommandName());
|
||||
response.setObjectName("uploadtemplatedirectdownloadcertificate");
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if (!hypervisor.equalsIgnoreCase("kvm")) {
|
||||
@ -74,10 +99,11 @@ public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd {
|
||||
|
||||
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);
|
||||
Pair<DirectDownloadCertificate, List<HostCertificateStatus>> uploadStatus =
|
||||
directDownloadManager.uploadCertificateToHosts(certificate, name, hypervisor, zoneId, hostId);
|
||||
DirectDownloadCertificate certificate = uploadStatus.first();
|
||||
List<HostCertificateStatus> hostStatus = uploadStatus.second();
|
||||
createResponse(certificate, hostStatus);
|
||||
} catch (Exception e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
@ -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 DirectDownloadCertificateHostStatusResponse 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,162 @@
|
||||
// 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.direct.download.DirectDownloadCertificate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@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.ZONE_NAME)
|
||||
@Param(description = "the zone name where the certificate is uploaded")
|
||||
private String zoneName;
|
||||
|
||||
@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")
|
||||
private String hypervisor;
|
||||
|
||||
@SerializedName(ApiConstants.HOSTS_MAP)
|
||||
@Param(description = "the hosts where the certificate is uploaded to", responseObject = HostResponse.class)
|
||||
private List<DirectDownloadCertificateHostStatusResponse> hostsMap;
|
||||
|
||||
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 getHypervisor() {
|
||||
return hypervisor;
|
||||
}
|
||||
|
||||
public void setHypervisor(String hypervisor) {
|
||||
this.hypervisor = hypervisor;
|
||||
}
|
||||
|
||||
public String getZoneName() {
|
||||
return zoneName;
|
||||
}
|
||||
|
||||
public void setZoneName(String zoneName) {
|
||||
this.zoneName = zoneName;
|
||||
}
|
||||
|
||||
public List<DirectDownloadCertificateHostStatusResponse> getHostsMap() {
|
||||
return hostsMap;
|
||||
}
|
||||
|
||||
public void setHostsMap(List<DirectDownloadCertificateHostStatusResponse> 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;
|
||||
}
|
||||
}
|
||||
@ -25,5 +25,6 @@ public interface DirectDownloadCertificate extends InternalIdentity, Identity {
|
||||
String getCertificate();
|
||||
String getAlias();
|
||||
Hypervisor.HypervisorType getHypervisorType();
|
||||
Long getZoneId();
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -17,17 +17,21 @@
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
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<Long> DirectDownloadCertificateUploadInterval = new ConfigKey<>("Advanced", Long.class,
|
||||
"direct.download.certificate.background.task.interval",
|
||||
@ -37,26 +41,66 @@ public interface DirectDownloadManager extends DirectDownloadService, PluggableS
|
||||
"Only certificates which have not been revoked from hosts are uploaded",
|
||||
false);
|
||||
|
||||
static final ConfigKey<Integer> DirectDownloadConnectTimeout = new ConfigKey<Integer>("Advanced", Integer.class,
|
||||
ConfigKey<Integer> DirectDownloadConnectTimeout = new ConfigKey<Integer>("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<Integer> DirectDownloadSocketTimeout = new ConfigKey<Integer>("Advanced", Integer.class,
|
||||
ConfigKey<Integer> DirectDownloadSocketTimeout = new ConfigKey<Integer>("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<Integer> DirectDownloadConnectionRequestTimeout = new ConfigKey<Integer>("Hidden", Integer.class,
|
||||
ConfigKey<Integer> DirectDownloadConnectionRequestTimeout = new ConfigKey<Integer>("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",
|
||||
true);
|
||||
|
||||
class HostCertificateStatus {
|
||||
public enum CertificateStatus {
|
||||
REVOKED, FAILED, SKIPPED, UPLOADED
|
||||
}
|
||||
|
||||
HostCertificateStatus(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;
|
||||
}
|
||||
}
|
||||
|
||||
DirectDownloadCertificate findDirectDownloadCertificateByIdOrHypervisorAndAlias(Long id, String alias, String hypervisor, Long zoneId);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
List<HostCertificateStatus> revokeCertificate(DirectDownloadCertificate certificate, Long zoneId, Long hostId);
|
||||
|
||||
List<DirectDownloadCertificate> listDirectDownloadCertificates(Long certificateId, Long zoneId);
|
||||
|
||||
List<DirectDownloadCertificateHostMap> getCertificateHostsMapping(Long certificateId);
|
||||
|
||||
/**
|
||||
* Upload client certificate to each running host
|
||||
* @return
|
||||
*/
|
||||
Pair<DirectDownloadCertificate, List<HostCertificateStatus>> uploadCertificateToHosts(String certificateCer, String certificateName, String hypervisor, Long zoneId, Long hostId);
|
||||
}
|
||||
|
||||
@ -23,4 +23,5 @@ import java.util.List;
|
||||
public interface DirectDownloadCertificateHostMapDao extends GenericDao<DirectDownloadCertificateHostMapVO, Long> {
|
||||
DirectDownloadCertificateHostMapVO findByCertificateAndHost(long certificateId, long hostId);
|
||||
List<DirectDownloadCertificateHostMapVO> listByCertificateId(long certificateId);
|
||||
List<DirectDownloadCertificateHostMapVO> listByCertificateIdAndRevoked(long certificateId, boolean revoked);
|
||||
}
|
||||
@ -29,6 +29,7 @@ public class DirectDownloadCertificateHostMapDaoImpl extends GenericDaoBase<Dire
|
||||
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 class DirectDownloadCertificateHostMapDaoImpl extends GenericDaoBase<Dire
|
||||
sc.setParameters("certificate_id", certificateId);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DirectDownloadCertificateHostMapVO> listByCertificateIdAndRevoked(long certificateId, boolean revoked) {
|
||||
SearchCriteria<DirectDownloadCertificateHostMapVO> sc = mapSearchBuilder.create();
|
||||
sc.setParameters("certificate_id", certificateId);
|
||||
sc.setParameters("revoked", revoked);
|
||||
return listBy(sc);
|
||||
}
|
||||
}
|
||||
@ -25,12 +25,12 @@ import javax.persistence.Table;
|
||||
|
||||
@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 class DirectDownloadCertificateHostMapVO {
|
||||
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 class DirectDownloadCertificateHostMapVO {
|
||||
this.hostId = hostId;
|
||||
}
|
||||
|
||||
public Long getCertificateId() {
|
||||
public long getCertificateId() {
|
||||
return certificateId;
|
||||
}
|
||||
|
||||
@ -74,8 +74,8 @@ public class DirectDownloadCertificateHostMapVO {
|
||||
this.certificateId = certificateId;
|
||||
}
|
||||
|
||||
public Boolean isRevoked() {
|
||||
return revoked;
|
||||
public boolean isRevoked() {
|
||||
return revoked != null && revoked;
|
||||
}
|
||||
|
||||
public void setRevoked(Boolean revoked) {
|
||||
|
||||
@ -21,6 +21,14 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cloud-framework-direct-download</artifactId>
|
||||
<name>Apache CloudStack Framework - Direct Download to Primary Storage</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-utils</artifactId>
|
||||
<version>4.17.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<parent>
|
||||
<artifactId>cloudstack-framework</artifactId>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
|
||||
@ -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<Boolean, String> provisionCertificate(long certificateId, long hostId);
|
||||
|
||||
/**
|
||||
* Sync the stored certificates to host with id 'hostId'
|
||||
|
||||
@ -18,6 +18,8 @@ package com.cloud.api;
|
||||
|
||||
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 java.util.stream.Collectors;
|
||||
|
||||
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;
|
||||
@ -67,6 +70,7 @@ import org.apache.cloudstack.api.response.ControlledViewEntityResponse;
|
||||
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;
|
||||
@ -118,6 +122,7 @@ import org.apache.cloudstack.api.response.ResourceCountResponse;
|
||||
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.DirectDownloadCertificateHostStatusResponse;
|
||||
import org.apache.cloudstack.api.response.RollingMaintenanceHostSkippedResponse;
|
||||
import org.apache.cloudstack.api.response.RollingMaintenanceHostUpdatedResponse;
|
||||
import org.apache.cloudstack.api.response.RollingMaintenanceResponse;
|
||||
@ -160,11 +165,15 @@ import org.apache.cloudstack.backup.BackupSchedule;
|
||||
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.direct.download.DirectDownloadManager;
|
||||
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;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
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;
|
||||
@ -363,6 +372,7 @@ import com.cloud.vm.snapshot.VMSnapshot;
|
||||
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 {
|
||||
|
||||
@ -4547,4 +4557,82 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
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();
|
||||
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());
|
||||
handleCertificateResponse(certificate.getCertificate(), response);
|
||||
response.setHypervisor(certificate.getHypervisorType().name());
|
||||
response.setObjectName("directdownloadcertificate");
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DirectDownloadCertificateHostStatusResponse> createDirectDownloadCertificateHostMapResponse(List<DirectDownloadCertificateHostMap> hostMappings) {
|
||||
if (CollectionUtils.isEmpty(hostMappings)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<DirectDownloadCertificateHostStatusResponse> responses = new ArrayList<>(hostMappings.size());
|
||||
for (DirectDownloadCertificateHostMap map : hostMappings) {
|
||||
DirectDownloadCertificateHostStatusResponse response = new DirectDownloadCertificateHostStatusResponse();
|
||||
HostVO host = ApiDBUtils.findHostById(map.getHostId());
|
||||
if (host != null) {
|
||||
response.setHostId(host.getUuid());
|
||||
response.setHostName(host.getName());
|
||||
}
|
||||
response.setStatus(map.isRevoked() ? CertificateStatus.REVOKED.name() : CertificateStatus.UPLOADED.name());
|
||||
response.setObjectName("directdownloadcertificatehoststatus");
|
||||
responses.add(response);
|
||||
}
|
||||
return responses;
|
||||
}
|
||||
|
||||
private DirectDownloadCertificateHostStatusResponse getDirectDownloadHostStatusResponseInternal(Host host, CertificateStatus status, String details) {
|
||||
DirectDownloadCertificateHostStatusResponse response = new DirectDownloadCertificateHostStatusResponse();
|
||||
if (host != null) {
|
||||
response.setHostId(host.getUuid());
|
||||
response.setHostName(host.getName());
|
||||
}
|
||||
response.setStatus(status.name());
|
||||
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<Boolean, String> 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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,6 +74,8 @@ import org.apache.cloudstack.api.command.admin.config.ListHypervisorCapabilities
|
||||
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.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;
|
||||
@ -3547,6 +3549,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
||||
cmdList.add(DeleteManagementNetworkIpRangeCmd.class);
|
||||
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);
|
||||
|
||||
@ -19,6 +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;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
@ -29,6 +30,7 @@ import java.security.cert.CertificateNotYetValidException;
|
||||
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 +41,8 @@ import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
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;
|
||||
@ -66,6 +70,7 @@ import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
|
||||
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;
|
||||
@ -448,7 +453,8 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uploadCertificateToHosts(String certificateCer, String alias, String hypervisor, Long zoneId, Long hostId) {
|
||||
public Pair<DirectDownloadCertificate, List<HostCertificateStatus>> 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");
|
||||
}
|
||||
@ -457,6 +463,10 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
|
||||
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);
|
||||
|
||||
@ -475,48 +485,55 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
|
||||
certificateVO = directDownloadCertificateDao.findByAlias(alias, hypervisorType, zoneId);
|
||||
if (certificateVO == null) {
|
||||
s_logger.info("Certificate must be uploaded on zone " + zoneId);
|
||||
return false;
|
||||
return new Pair<>(certificateVO, 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<HostCertificateStatus> 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() + ")";
|
||||
s_logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
if (host == null) {
|
||||
continue;
|
||||
}
|
||||
hostCount++;
|
||||
HostCertificateStatus hostStatus;
|
||||
Pair<Boolean, String> 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);
|
||||
failed++;
|
||||
hostStatus = new HostCertificateStatus(CertificateStatus.FAILED, host, result.second());
|
||||
} else {
|
||||
success++;
|
||||
hostStatus = new HostCertificateStatus(CertificateStatus.UPLOADED, host, "");
|
||||
}
|
||||
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 new Pair<>(certificateVO, 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);
|
||||
}
|
||||
private Pair<Boolean, String> setupCertificateOnHost(DirectDownloadCertificate certificate, long hostId) {
|
||||
String certificateStr = certificate.getCertificate();
|
||||
String alias = certificate.getAlias();
|
||||
long certificateId = certificate.getId();
|
||||
|
||||
String certificate = certificateVO.getCertificate();
|
||||
String alias = certificateVO.getAlias();
|
||||
|
||||
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<Boolean, String> 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);
|
||||
@ -528,8 +545,25 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
|
||||
DirectDownloadCertificateHostMapVO mapVO = new DirectDownloadCertificateHostMapVO(certificateId, hostId);
|
||||
directDownloadCertificateHostMapDao.persist(mapVO);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Upload and import certificate to hostId on keystore
|
||||
*/
|
||||
public Pair<Boolean, String> 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
|
||||
@ -549,8 +583,9 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
|
||||
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<Boolean, String> 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 {
|
||||
@ -565,52 +600,127 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
|
||||
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<DirectDownloadCertificateHostMapVO> maps = null;
|
||||
private List<DirectDownloadCertificateHostMapVO> getCertificateHostMappings(DirectDownloadCertificate certificate, Long hostId) {
|
||||
List<DirectDownloadCertificateHostMapVO> maps;
|
||||
if (hostId == null) {
|
||||
maps = directDownloadCertificateHostMapDao.listByCertificateId(certificateVO.getId());
|
||||
maps = directDownloadCertificateHostMapDao.listByCertificateIdAndRevoked(certificate.getId(), false);
|
||||
} else {
|
||||
DirectDownloadCertificateHostMapVO hostMap = directDownloadCertificateHostMapDao.findByCertificateAndHost(certificateVO.getId(), hostId);
|
||||
DirectDownloadCertificateHostMapVO hostMap = directDownloadCertificateHostMapDao.findByCertificateAndHost(certificate.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 " + 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 " + certificate.getAlias() + " was already revoked from host " + hostId + " skipping it");
|
||||
return new LinkedList<>();
|
||||
}
|
||||
maps = Collections.singletonList(hostMap);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
s_logger.info("Certificate " + certificateAlias + " revoked from host " + mappingHostId);
|
||||
map.setRevoked(true);
|
||||
directDownloadCertificateHostMapDao.update(map.getId(), map);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return maps;
|
||||
}
|
||||
|
||||
protected boolean revokeCertificateAliasFromHost(String alias, Long hostId) {
|
||||
@Override
|
||||
public DirectDownloadCertificate findDirectDownloadCertificateByIdOrHypervisorAndAlias(Long id, String alias, String hypervisor, Long zoneId) {
|
||||
DirectDownloadCertificateVO certificateVO;
|
||||
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 hypervisor and certificate alias or certificate ID");
|
||||
}
|
||||
if (certificateVO == null) {
|
||||
throw new CloudRuntimeException("Could not find certificate " +
|
||||
(id != null ? "with ID " + id : "with alias " + alias + " and hypervisor " + hypervisor));
|
||||
}
|
||||
return certificateVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HostCertificateStatus> 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=" + certificate.getZoneId() + " instead of the zone with ID=" + zoneId);
|
||||
}
|
||||
|
||||
List<HostCertificateStatus> hostsList = new ArrayList<>();
|
||||
List<DirectDownloadCertificateHostMapVO> maps = getCertificateHostMappings(certificate, hostId);
|
||||
if (CollectionUtils.isEmpty(maps)) {
|
||||
return hostsList;
|
||||
}
|
||||
|
||||
int success = 0;
|
||||
int failed = 0;
|
||||
int skipped = 0;
|
||||
s_logger.info("Attempting to revoke certificate alias: " + certificateAlias + " from " + maps.size() + " hosts");
|
||||
for (DirectDownloadCertificateHostMapVO map : maps) {
|
||||
Long mappingHostId = map.getHostId();
|
||||
HostVO host = hostDao.findById(mappingHostId);
|
||||
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 HostCertificateStatus(CertificateStatus.SKIPPED, host, reason);
|
||||
hostsList.add(hostStatus);
|
||||
}
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
Pair<Boolean, String> result = revokeCertificateAliasFromHost(certificateAlias, mappingHostId);
|
||||
if (!result.first()) {
|
||||
String msg = "Could not revoke certificate from host: " + mappingHostId + ": " + result.second();
|
||||
s_logger.error(msg);
|
||||
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(CertificateStatus.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 hostsList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DirectDownloadCertificate> listDirectDownloadCertificates(Long certificateId, Long zoneId) {
|
||||
if (zoneId != null && dataCenterDao.findById(zoneId) == null) {
|
||||
throw new InvalidParameterValueException("Cannot find a zone with ID = " + zoneId);
|
||||
}
|
||||
List<DirectDownloadCertificate> certificates = new LinkedList<>();
|
||||
if (certificateId != null) {
|
||||
certificates.add(directDownloadCertificateDao.findById(certificateId));
|
||||
} else if (zoneId != null) {
|
||||
certificates.addAll(directDownloadCertificateDao.listByZone(zoneId));
|
||||
} else {
|
||||
certificates.addAll(directDownloadCertificateDao.listAll());
|
||||
}
|
||||
return certificates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DirectDownloadCertificateHostMap> getCertificateHostsMapping(Long certificateId) {
|
||||
if (certificateId == null) {
|
||||
throw new InvalidParameterValueException("Please specify a certificate ID");
|
||||
}
|
||||
return new LinkedList<>(directDownloadCertificateHostMapDao.listByCertificateId(certificateId));
|
||||
}
|
||||
|
||||
protected Pair<Boolean, String> 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
|
||||
@ -692,10 +802,13 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
|
||||
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<Boolean, String> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ import java.text.SimpleDateFormat;
|
||||
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 class ApiResponseHelperTest {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
@ -28,7 +28,8 @@ from marvin.lib.base import (ServiceOffering,
|
||||
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
|
||||
|
||||
@ -136,9 +137,13 @@ class TestUploadDirectDownloadCertificates(cloudstackTestCase):
|
||||
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.hypervisor = self.hypervisor
|
||||
revokecmd.name = cmd.name
|
||||
revokecmd.id = cert.id
|
||||
revokecmd.zoneid = self.zone.id
|
||||
|
||||
try:
|
||||
@ -149,6 +154,7 @@ class TestUploadDirectDownloadCertificates(cloudstackTestCase):
|
||||
return
|
||||
|
||||
|
||||
|
||||
class TestDirectDownloadTemplates(cloudstackTestCase):
|
||||
|
||||
@classmethod
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user