mirror of https://github.com/apache/cloudstack.git
Merge f90dc4df6f into 5893ba5a8c
This commit is contained in:
commit
fbbee6bec2
|
|
@ -71,6 +71,11 @@
|
|||
<artifactId>cloud-framework-direct-download</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-kms</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ import org.apache.cloudstack.gpu.GpuCard;
|
|||
import org.apache.cloudstack.gpu.GpuDevice;
|
||||
import org.apache.cloudstack.gpu.VgpuProfile;
|
||||
import org.apache.cloudstack.ha.HAConfig;
|
||||
import org.apache.cloudstack.kms.HSMProfile;
|
||||
import org.apache.cloudstack.kms.KMSKey;
|
||||
import org.apache.cloudstack.network.BgpPeer;
|
||||
import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap;
|
||||
import org.apache.cloudstack.quota.QuotaTariff;
|
||||
|
|
@ -271,6 +273,20 @@ public class EventTypes {
|
|||
public static final String EVENT_CA_CERTIFICATE_REVOKE = "CA.CERTIFICATE.REVOKE";
|
||||
public static final String EVENT_CA_CERTIFICATE_PROVISION = "CA.CERTIFICATE.PROVISION";
|
||||
|
||||
// KMS (Key Management Service) events
|
||||
public static final String EVENT_KMS_KEY_WRAP = "KMS.KEY.WRAP";
|
||||
public static final String EVENT_KMS_KEY_UNWRAP = "KMS.KEY.UNWRAP";
|
||||
public static final String EVENT_KMS_KEY_CREATE = "KMS.KEY.CREATE";
|
||||
public static final String EVENT_KMS_KEY_UPDATE = "KMS.KEY.UPDATE";
|
||||
public static final String EVENT_KMS_KEY_ROTATE = "KMS.KEY.ROTATE";
|
||||
public static final String EVENT_KMS_KEY_DELETE = "KMS.KEY.DELETE";
|
||||
public static final String EVENT_VOLUME_MIGRATE_TO_KMS = "VOLUME.MIGRATE.TO.KMS";
|
||||
|
||||
// HSM Profile events
|
||||
public static final String EVENT_HSM_PROFILE_CREATE = "HSM.PROFILE.CREATE";
|
||||
public static final String EVENT_HSM_PROFILE_UPDATE = "HSM.PROFILE.UPDATE";
|
||||
public static final String EVENT_HSM_PROFILE_DELETE = "HSM.PROFILE.DELETE";
|
||||
|
||||
// Account events
|
||||
public static final String EVENT_ACCOUNT_ENABLE = "ACCOUNT.ENABLE";
|
||||
public static final String EVENT_ACCOUNT_DISABLE = "ACCOUNT.DISABLE";
|
||||
|
|
@ -1015,6 +1031,20 @@ public class EventTypes {
|
|||
entityEventDetails.put(EVENT_VOLUME_RECOVER, Volume.class);
|
||||
entityEventDetails.put(EVENT_VOLUME_CHANGE_DISK_OFFERING, Volume.class);
|
||||
|
||||
// KMS Key Events
|
||||
entityEventDetails.put(EVENT_KMS_KEY_CREATE, KMSKey.class);
|
||||
entityEventDetails.put(EVENT_KMS_KEY_UPDATE, KMSKey.class);
|
||||
entityEventDetails.put(EVENT_KMS_KEY_UNWRAP, KMSKey.class);
|
||||
entityEventDetails.put(EVENT_KMS_KEY_WRAP, KMSKey.class);
|
||||
entityEventDetails.put(EVENT_KMS_KEY_DELETE, KMSKey.class);
|
||||
entityEventDetails.put(EVENT_KMS_KEY_ROTATE, KMSKey.class);
|
||||
entityEventDetails.put(EVENT_VOLUME_MIGRATE_TO_KMS, KMSKey.class);
|
||||
|
||||
// HSM Profile Events
|
||||
entityEventDetails.put(EVENT_HSM_PROFILE_CREATE, HSMProfile.class);
|
||||
entityEventDetails.put(EVENT_HSM_PROFILE_UPDATE, HSMProfile.class);
|
||||
entityEventDetails.put(EVENT_HSM_PROFILE_DELETE, HSMProfile.class);
|
||||
|
||||
// Domains
|
||||
entityEventDetails.put(EVENT_DOMAIN_CREATE, Domain.class);
|
||||
entityEventDetails.put(EVENT_DOMAIN_DELETE, Domain.class);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public class DiskOfferingInfo {
|
|||
private Long _size;
|
||||
private Long _minIops;
|
||||
private Long _maxIops;
|
||||
private Long _kmsKeyId;
|
||||
|
||||
public DiskOfferingInfo() {
|
||||
}
|
||||
|
|
@ -38,6 +39,14 @@ public class DiskOfferingInfo {
|
|||
_maxIops = maxIops;
|
||||
}
|
||||
|
||||
public DiskOfferingInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops, Long kmsKeyId) {
|
||||
_diskOffering = diskOffering;
|
||||
_size = size;
|
||||
_minIops = minIops;
|
||||
_maxIops = maxIops;
|
||||
_kmsKeyId = kmsKeyId;
|
||||
}
|
||||
|
||||
public void setDiskOffering(DiskOffering diskOffering) {
|
||||
_diskOffering = diskOffering;
|
||||
}
|
||||
|
|
@ -69,4 +78,12 @@ public class DiskOfferingInfo {
|
|||
public Long getMaxIops() {
|
||||
return _maxIops;
|
||||
}
|
||||
|
||||
public void setKmsKeyId(Long kmsKeyId) {
|
||||
_kmsKeyId = kmsKeyId;
|
||||
}
|
||||
|
||||
public Long getKmsKeyId() {
|
||||
return _kmsKeyId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,6 +275,14 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
|
|||
|
||||
void setPassphraseId(Long id);
|
||||
|
||||
Long getKmsKeyId();
|
||||
|
||||
void setKmsKeyId(Long id);
|
||||
|
||||
Long getKmsWrappedKeyId();
|
||||
|
||||
void setKmsWrappedKeyId(Long id);
|
||||
|
||||
String getEncryptFormat();
|
||||
|
||||
void setEncryptFormat(String encryptFormat);
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ public interface UserVmService {
|
|||
String userData, Long userDataId, String userDataDetails, List<String> sshKeyPairs, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameter, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException,
|
||||
Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException,
|
||||
ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
|
||||
|
||||
/**
|
||||
|
|
@ -305,7 +305,7 @@ public interface UserVmService {
|
|||
List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List<VmDiskInfo> dataDiskInfoList, String group, HypervisorType hypervisor,
|
||||
HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List<String> sshKeyPairs, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, String vmType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
|
||||
|
||||
/**
|
||||
* Creates a User VM in Advanced Zone (Security Group feature is disabled)
|
||||
|
|
@ -377,7 +377,7 @@ public interface UserVmService {
|
|||
String hostName, String displayName, Long diskOfferingId, Long diskSize, List<VmDiskInfo> dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
|
||||
Long userDataId, String userDataDetails, List<String> sshKeyPairs, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List<Long> affinityGroupIdList,
|
||||
Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Map<String, String> templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot)
|
||||
Map<String, String> templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, Volume volume, Snapshot snapshot)
|
||||
|
||||
throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ public class VmDiskInfo extends DiskOfferingInfo {
|
|||
_deviceId = deviceId;
|
||||
}
|
||||
|
||||
public VmDiskInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops, Long deviceId, Long kmsKeyId) {
|
||||
super(diskOffering, size, minIops, maxIops, kmsKeyId);
|
||||
_deviceId = deviceId;
|
||||
}
|
||||
|
||||
public Long getDeviceId() {
|
||||
return _deviceId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,9 @@ public enum ApiCommandResourceType {
|
|||
KubernetesSupportedVersion(null),
|
||||
SharedFS(org.apache.cloudstack.storage.sharedfs.SharedFS.class),
|
||||
Extension(org.apache.cloudstack.extension.Extension.class),
|
||||
ExtensionCustomAction(org.apache.cloudstack.extension.ExtensionCustomAction.class);
|
||||
ExtensionCustomAction(org.apache.cloudstack.extension.ExtensionCustomAction.class),
|
||||
KmsKey(org.apache.cloudstack.kms.KMSKey.class),
|
||||
HsmProfile(org.apache.cloudstack.kms.HSMProfile.class);
|
||||
|
||||
private final Class<?> clazz;
|
||||
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ public class ApiConstants {
|
|||
public static final String UTILIZATION = "utilization";
|
||||
public static final String DRIVER = "driver";
|
||||
public static final String ROOT_DISK_SIZE = "rootdisksize";
|
||||
public static final String ROOT_DISK_KMS_KEY_ID = "rootdiskkmskeyid";
|
||||
public static final String DHCP_OPTIONS_NETWORK_LIST = "dhcpoptionsnetworklist";
|
||||
public static final String DHCP_OPTIONS = "dhcpoptions";
|
||||
public static final String DHCP_PREFIX = "dhcp:";
|
||||
|
|
@ -873,7 +874,14 @@ public class ApiConstants {
|
|||
public static final String ITERATIONS = "iterations";
|
||||
public static final String SORT_BY = "sortby";
|
||||
public static final String CHANGE_CIDR = "changecidr";
|
||||
public static final String HSM_PROFILE = "hsmprofile";
|
||||
public static final String HSM_PROFILE_ID = "hsmprofileid";
|
||||
public static final String PURPOSE = "purpose";
|
||||
public static final String KMS_KEY = "kmskey";
|
||||
public static final String KMS_KEY_ID = "kmskeyid";
|
||||
public static final String KMS_KEY_VERSION = "kmskeyversion";
|
||||
public static final String KEK_LABEL = "keklabel";
|
||||
public static final String KEY_BITS = "keybits";
|
||||
public static final String IS_TAGGED = "istagged";
|
||||
public static final String INSTANCE_NAME = "instancename";
|
||||
public static final String CONSIDER_LAST_HOST = "considerlasthost";
|
||||
|
|
|
|||
|
|
@ -281,7 +281,8 @@ public interface ResponseGenerator {
|
|||
|
||||
List<UserVmResponse> createUserVmResponse(ResponseView view, String objectName, UserVm... userVms);
|
||||
|
||||
List<UserVmResponse> createUserVmResponse(ResponseView view, String objectName, EnumSet<VMDetails> details, UserVm... userVms);
|
||||
List<UserVmResponse> createUserVmResponse(ResponseView view, String objectName, EnumSet<VMDetails> details,
|
||||
UserVm... userVms);
|
||||
|
||||
SystemVmResponse createSystemVmResponse(VirtualMachine systemVM);
|
||||
|
||||
|
|
@ -307,11 +308,13 @@ public interface ResponseGenerator {
|
|||
|
||||
LoadBalancerResponse createLoadBalancerResponse(LoadBalancer loadBalancer);
|
||||
|
||||
LBStickinessResponse createLBStickinessPolicyResponse(List<? extends StickinessPolicy> stickinessPolicies, LoadBalancer lb);
|
||||
LBStickinessResponse createLBStickinessPolicyResponse(List<? extends StickinessPolicy> stickinessPolicies,
|
||||
LoadBalancer lb);
|
||||
|
||||
LBStickinessResponse createLBStickinessPolicyResponse(StickinessPolicy stickinessPolicy, LoadBalancer lb);
|
||||
|
||||
LBHealthCheckResponse createLBHealthCheckPolicyResponse(List<? extends HealthCheckPolicy> healthcheckPolicies, LoadBalancer lb);
|
||||
LBHealthCheckResponse createLBHealthCheckPolicyResponse(List<? extends HealthCheckPolicy> healthcheckPolicies,
|
||||
LoadBalancer lb);
|
||||
|
||||
LBHealthCheckResponse createLBHealthCheckPolicyResponse(HealthCheckPolicy healthcheckPolicy, LoadBalancer lb);
|
||||
|
||||
|
|
@ -319,7 +322,8 @@ public interface ResponseGenerator {
|
|||
|
||||
PodResponse createMinimalPodResponse(Pod pod);
|
||||
|
||||
ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities, Boolean showResourceIcon);
|
||||
ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities,
|
||||
Boolean showResourceIcon);
|
||||
|
||||
DataCenterGuestIpv6PrefixResponse createDataCenterGuestIpv6PrefixResponse(DataCenterGuestIpv6Prefix prefix);
|
||||
|
||||
|
|
@ -361,7 +365,8 @@ public interface ResponseGenerator {
|
|||
|
||||
List<TemplateResponse> createTemplateResponses(ResponseView view, long templateId, Long zoneId, boolean readyOnly);
|
||||
|
||||
List<TemplateResponse> createTemplateResponses(ResponseView view, long templateId, Long snapshotId, Long volumeId, boolean readyOnly);
|
||||
List<TemplateResponse> createTemplateResponses(ResponseView view, long templateId, Long snapshotId, Long volumeId,
|
||||
boolean readyOnly);
|
||||
|
||||
SecurityGroupResponse createSecurityGroupResponseFromSecurityGroupRule(List<? extends SecurityRule> securityRules);
|
||||
|
||||
|
|
@ -380,14 +385,15 @@ public interface ResponseGenerator {
|
|||
TemplateResponse createTemplateUpdateResponse(ResponseView view, VirtualMachineTemplate result);
|
||||
|
||||
List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachineTemplate result,
|
||||
Long zoneId, boolean readyOnly);
|
||||
Long zoneId, boolean readyOnly);
|
||||
|
||||
List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachineTemplate result,
|
||||
List<Long> zoneIds, boolean readyOnly);
|
||||
List<Long> zoneIds, boolean readyOnly);
|
||||
|
||||
List<CapacityResponse> createCapacityResponse(List<? extends Capacity> result, DecimalFormat format);
|
||||
|
||||
TemplatePermissionsResponse createTemplatePermissionsResponse(ResponseView view, List<String> accountNames, Long id);
|
||||
TemplatePermissionsResponse createTemplatePermissionsResponse(ResponseView view, List<String> accountNames,
|
||||
Long id);
|
||||
|
||||
AsyncJobResponse queryJobResult(QueryAsyncJobResultCmd cmd);
|
||||
|
||||
|
|
@ -401,7 +407,8 @@ public interface ResponseGenerator {
|
|||
|
||||
Long getSecurityGroupId(String groupName, long accountId);
|
||||
|
||||
List<TemplateResponse> createIsoResponses(ResponseView view, VirtualMachineTemplate iso, Long zoneId, boolean readyOnly);
|
||||
List<TemplateResponse> createIsoResponses(ResponseView view, VirtualMachineTemplate iso, Long zoneId,
|
||||
boolean readyOnly);
|
||||
|
||||
ProjectResponse createProjectResponse(Project project);
|
||||
|
||||
|
|
@ -502,13 +509,15 @@ public interface ResponseGenerator {
|
|||
|
||||
GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor osHypervisor);
|
||||
|
||||
HypervisorGuestOsNamesResponse createHypervisorGuestOSNamesResponse(List<Pair<String, String>> hypervisorGuestOsNames);
|
||||
HypervisorGuestOsNamesResponse createHypervisorGuestOSNamesResponse(
|
||||
List<Pair<String, String>> hypervisorGuestOsNames);
|
||||
|
||||
SnapshotScheduleResponse createSnapshotScheduleResponse(SnapshotSchedule sched);
|
||||
|
||||
UsageRecordResponse createUsageResponse(Usage usageRecord);
|
||||
|
||||
UsageRecordResponse createUsageResponse(Usage usageRecord, Map<String, Set<ResourceTagResponse>> resourceTagResponseMap, boolean oldFormat);
|
||||
UsageRecordResponse createUsageResponse(Usage usageRecord,
|
||||
Map<String, Set<ResourceTagResponse>> resourceTagResponseMap, boolean oldFormat);
|
||||
|
||||
public Map<String, Set<ResourceTagResponse>> getUsageResourceTags();
|
||||
|
||||
|
|
@ -520,7 +529,8 @@ public interface ResponseGenerator {
|
|||
|
||||
public NicResponse createNicResponse(Nic result);
|
||||
|
||||
ApplicationLoadBalancerResponse createLoadBalancerContainerReponse(ApplicationLoadBalancerRule lb, Map<Ip, UserVm> lbInstances);
|
||||
ApplicationLoadBalancerResponse createLoadBalancerContainerReponse(ApplicationLoadBalancerRule lb,
|
||||
Map<Ip, UserVm> lbInstances);
|
||||
|
||||
AffinityGroupResponse createAffinityGroupResponse(AffinityGroup group);
|
||||
|
||||
|
|
@ -546,9 +556,12 @@ public interface ResponseGenerator {
|
|||
|
||||
ManagementServerResponse createManagementResponse(ManagementServerHost mgmt);
|
||||
|
||||
List<RouterHealthCheckResultResponse> createHealthCheckResponse(VirtualMachine router, List<RouterHealthCheckResult> healthCheckResults);
|
||||
List<RouterHealthCheckResultResponse> createHealthCheckResponse(VirtualMachine router,
|
||||
List<RouterHealthCheckResult> healthCheckResults);
|
||||
|
||||
RollingMaintenanceResponse createRollingMaintenanceResponse(Boolean success, String details, List<RollingMaintenanceManager.HostUpdated> hostsUpdated, List<RollingMaintenanceManager.HostSkipped> hostsSkipped);
|
||||
RollingMaintenanceResponse createRollingMaintenanceResponse(Boolean success, String details,
|
||||
List<RollingMaintenanceManager.HostUpdated> hostsUpdated,
|
||||
List<RollingMaintenanceManager.HostSkipped> hostsSkipped);
|
||||
|
||||
ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon);
|
||||
|
||||
|
|
@ -558,11 +571,14 @@ public interface ResponseGenerator {
|
|||
|
||||
DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate);
|
||||
|
||||
List<DirectDownloadCertificateHostStatusResponse> createDirectDownloadCertificateHostMapResponse(List<DirectDownloadCertificateHostMap> hostMappings);
|
||||
List<DirectDownloadCertificateHostStatusResponse> createDirectDownloadCertificateHostMapResponse(
|
||||
List<DirectDownloadCertificateHostMap> hostMappings);
|
||||
|
||||
DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(DirectDownloadManager.HostCertificateStatus status);
|
||||
DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateHostStatusResponse(
|
||||
DirectDownloadManager.HostCertificateStatus status);
|
||||
|
||||
DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId, Long hostId, Pair<Boolean, String> result);
|
||||
DirectDownloadCertificateHostStatusResponse createDirectDownloadCertificateProvisionResponse(Long certificateId,
|
||||
Long hostId, Pair<Boolean, String> result);
|
||||
|
||||
FirewallResponse createIpv6FirewallRuleResponse(FirewallRule acl);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,142 @@
|
|||
// 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.kms;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.AsyncJobResponse;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.kms.KMSKey;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
@APICommand(name = "migrateVolumesToKMS",
|
||||
description = "Migrates encrypted volumes to KMS",
|
||||
responseObject = AsyncJobResponse.class,
|
||||
since = "4.23.0",
|
||||
authorized = {RoleType.Admin},
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false)
|
||||
public class MigrateVolumesToKMSCmd extends BaseAsyncCmd {
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
description = "Zone ID")
|
||||
private Long zoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.ACCOUNT,
|
||||
type = CommandType.STRING,
|
||||
description = "Migrate volumes for specific account")
|
||||
private String accountName;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = DomainResponse.class,
|
||||
description = "Domain ID")
|
||||
private Long domainId;
|
||||
|
||||
@Parameter(name = ApiConstants.VOLUME_IDS,
|
||||
type = CommandType.LIST,
|
||||
collectionType = CommandType.UUID,
|
||||
entityType = VolumeResponse.class,
|
||||
description = "List of volume IDs to migrate")
|
||||
private List<Long> volumeIds;
|
||||
|
||||
@Parameter(name = ApiConstants.KMS_KEY_ID,
|
||||
required = true,
|
||||
type = CommandType.UUID,
|
||||
entityType = KMSKeyResponse.class,
|
||||
description = "KMS Key ID to use for migrating volumes")
|
||||
private Long kmsKeyId;
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
public Long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
public List<Long> getVolumeIds() {
|
||||
return volumeIds;
|
||||
}
|
||||
|
||||
public Long getKmsKeyId() {
|
||||
return kmsKeyId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
try {
|
||||
kmsManager.migrateVolumesToKMS(this);
|
||||
} catch (KMSException e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
|
||||
"Failed to migrate volumes to KMS: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
KMSKey key = _entityMgr.findById(KMSKey.class, kmsKeyId);
|
||||
if (key != null) {
|
||||
return key.getAccountId();
|
||||
}
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return com.cloud.event.EventTypes.EVENT_VOLUME_MIGRATE_TO_KMS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Migrating volumes to KMS for zone: " + _uuidMgr.getUuid(DataCenter.class, zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KmsKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return kmsKeyId;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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.user.kms;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
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.command.user.UserCmd;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.HSMProfileResponse;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "createKMSKey",
|
||||
description = "Creates a new KMS key (Key Encryption Key) for encryption",
|
||||
responseObject = KMSKeyResponse.class,
|
||||
since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false)
|
||||
public class CreateKMSKeyCmd extends BaseCmd implements UserCmd {
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.NAME,
|
||||
required = true,
|
||||
type = CommandType.STRING,
|
||||
description = "Name of the KMS key")
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.DESCRIPTION,
|
||||
type = CommandType.STRING,
|
||||
description = "Description of the KMS key")
|
||||
private String description;
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID,
|
||||
required = true,
|
||||
type = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
description = "Zone ID where the key will be valid")
|
||||
private Long zoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.ACCOUNT,
|
||||
type = CommandType.STRING,
|
||||
description = "Account name (for creating keys for child accounts - requires domain admin or admin)")
|
||||
private String accountName;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = DomainResponse.class,
|
||||
description = "Domain ID (for creating keys for child accounts - requires domain admin or admin)")
|
||||
private Long domainId;
|
||||
|
||||
@Parameter(name = ApiConstants.PROJECT_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ProjectResponse.class,
|
||||
description = "ID of the project to create the KMS key for")
|
||||
private Long projectId;
|
||||
|
||||
@Parameter(name = ApiConstants.KEY_BITS,
|
||||
type = CommandType.INTEGER,
|
||||
description = "Key size in bits: 128, 192, or 256 (default: 256)")
|
||||
private Integer keyBits;
|
||||
|
||||
@Parameter(name = ApiConstants.HSM_PROFILE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = HSMProfileResponse.class,
|
||||
required = true,
|
||||
description = "ID of HSM profile to create key in")
|
||||
private Long hsmProfileId;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
public Long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public Integer getKeyBits() {
|
||||
return keyBits != null ? keyBits : 256;
|
||||
}
|
||||
|
||||
public Long getHsmProfileId() {
|
||||
return hsmProfileId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceAllocationException {
|
||||
try {
|
||||
KMSKeyResponse response = kmsManager.createKMSKey(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (KMSException e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
|
||||
"Failed to create KMS key: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true);
|
||||
if (accountId != null) {
|
||||
return accountId;
|
||||
}
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KmsKey;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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.user.kms;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.kms.KMSKey;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "deleteKMSKey",
|
||||
description = "Deletes a KMS key (only if not in use)",
|
||||
responseObject = SuccessResponse.class,
|
||||
since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false)
|
||||
public class DeleteKMSKeyCmd extends BaseAsyncCmd implements UserCmd {
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID,
|
||||
required = true,
|
||||
type = CommandType.UUID,
|
||||
entityType = KMSKeyResponse.class,
|
||||
description = "The UUID of the KMS key to delete")
|
||||
private Long id;
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
try {
|
||||
SuccessResponse response = kmsManager.deleteKMSKey(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (KMSException e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
|
||||
"Failed to delete KMS key: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
KMSKey key = _entityMgr.findById(KMSKey.class, id);
|
||||
if (key != null) {
|
||||
return key.getAccountId();
|
||||
}
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KmsKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_KMS_KEY_DELETE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "deleting KMS key: " + getId();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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.user.kms;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||
import org.apache.cloudstack.api.response.HSMProfileResponse;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "listKMSKeys",
|
||||
description = "Lists KMS keys available to the caller",
|
||||
responseObject = KMSKeyResponse.class,
|
||||
responseView = ResponseView.Restricted,
|
||||
since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false)
|
||||
public class ListKMSKeysCmd extends BaseListProjectAndAccountResourcesCmd implements UserCmd {
|
||||
private static final String s_name = "listkmskeysresponse";
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = KMSKeyResponse.class,
|
||||
description = "List KMS key by UUID")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
description = "Filter by zone ID")
|
||||
private Long zoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.ENABLED,
|
||||
type = CommandType.BOOLEAN,
|
||||
description = "Filter by enabled status")
|
||||
private Boolean enabled;
|
||||
|
||||
@Parameter(name = ApiConstants.HSM_PROFILE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = HSMProfileResponse.class,
|
||||
description = "Filter by HSM profile ID")
|
||||
private Long hsmProfileId;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public Long getHsmProfileId() {
|
||||
return hsmProfileId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ListResponse<KMSKeyResponse> listResponse = kmsManager.listKMSKeys(this);
|
||||
listResponse.setResponseName(getCommandName());
|
||||
setResponseObject(listResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return s_name;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
// 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.user.kms;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.AsyncJobResponse;
|
||||
import org.apache.cloudstack.api.response.HSMProfileResponse;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.kms.KMSKey;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "rotateKMSKey",
|
||||
description = "Rotates KMS key (KEK) by creating new version and scheduling gradual re-encryption",
|
||||
responseObject = AsyncJobResponse.class,
|
||||
since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false)
|
||||
public class RotateKMSKeyCmd extends BaseAsyncCmd {
|
||||
private static final String s_name = "rotatekmskeyresponse";
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID,
|
||||
required = true,
|
||||
type = CommandType.UUID,
|
||||
entityType = KMSKeyResponse.class,
|
||||
description = "KMS Key UUID to rotate")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.KEY_BITS,
|
||||
type = CommandType.INTEGER,
|
||||
description = "Key size for new KEK (default: same as current)")
|
||||
private Integer keyBits;
|
||||
|
||||
@Parameter(name = ApiConstants.HSM_PROFILE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = HSMProfileResponse.class,
|
||||
description = "The target HSM profile ID for the new KEK version. If provided, migrates the key to "
|
||||
+ "this HSM.")
|
||||
private Long hsmProfileId;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Integer getKeyBits() {
|
||||
return keyBits;
|
||||
}
|
||||
|
||||
public Long getHsmProfileId() {
|
||||
return hsmProfileId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
try {
|
||||
kmsManager.rotateKMSKey(this);
|
||||
SuccessResponse response = new SuccessResponse();
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (KMSException e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
|
||||
"Failed to rotate KMS key: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return s_name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
KMSKey key = _entityMgr.findById(KMSKey.class, id);
|
||||
if (key != null) {
|
||||
return key.getAccountId();
|
||||
}
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return com.cloud.event.EventTypes.EVENT_KMS_KEY_ROTATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Rotating KMS key: " + _uuidMgr.getUuid(KMSKey.class, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KmsKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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.user.kms;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "updateKMSKey",
|
||||
description = "Updates KMS key name, description, or enabled status",
|
||||
responseObject = KMSKeyResponse.class,
|
||||
since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false)
|
||||
public class UpdateKMSKeyCmd extends BaseAsyncCmd implements UserCmd {
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID,
|
||||
required = true,
|
||||
type = CommandType.UUID,
|
||||
entityType = KMSKeyResponse.class,
|
||||
description = "The UUID of the KMS key to update")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.NAME,
|
||||
type = CommandType.STRING,
|
||||
description = "New name for the key")
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.DESCRIPTION,
|
||||
type = CommandType.STRING,
|
||||
description = "New description for the key")
|
||||
private String description;
|
||||
|
||||
@Parameter(name = ApiConstants.ENABLED,
|
||||
type = CommandType.BOOLEAN,
|
||||
description = "whether the key should be enabled")
|
||||
private Boolean enabled;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
try {
|
||||
KMSKeyResponse response = kmsManager.updateKMSKey(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (KMSException e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
|
||||
"Failed to update KMS key: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.KmsKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_KMS_KEY_UPDATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "updating KMS key: " + getId();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
// 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.user.kms.hsm;
|
||||
|
||||
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.StringUtils;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
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.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.HSMProfileResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.kms.HSMProfile;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Map;
|
||||
|
||||
@APICommand(name = "createHSMProfile", description = "Creates a new HSM profile", responseObject = HSMProfileResponse.class,
|
||||
requestHasSensitiveInfo = true, responseHasSensitiveInfo = true, since = "4.23.0",
|
||||
authorized = { RoleType.Admin })
|
||||
public class CreateHSMProfileCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true,
|
||||
description = "the name of the HSM profile")
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING,
|
||||
description = "the protocol of the HSM profile (PKCS11, KMIP, etc.). Default is 'pkcs11'")
|
||||
private String protocol;
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class,
|
||||
description = "the zone ID where the HSM profile is available. If null, global scope (for admin only)")
|
||||
private Long zoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class,
|
||||
description = "the domain ID where the HSM profile is available")
|
||||
private Long domainId;
|
||||
|
||||
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING,
|
||||
description = "the account name of the HSM profile owner. Must be used with domainId.")
|
||||
private String accountName;
|
||||
|
||||
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class,
|
||||
description = "the ID of the project to add the HSM profile for")
|
||||
private Long projectId;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN,
|
||||
description = "whether this is a public HSM profile available to all users globally (root admin only). "
|
||||
+ "Default is false")
|
||||
private Boolean isPublic;
|
||||
|
||||
@Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, description = "the vendor name of the HSM")
|
||||
private String vendorName;
|
||||
|
||||
@Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP,
|
||||
description = "HSM configuration details (protocol specific)")
|
||||
private Map<String, String> details;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
if (StringUtils.isBlank(protocol)) {
|
||||
return "pkcs11";
|
||||
}
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public Long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public Boolean getIsPublic() {
|
||||
return isPublic != null && isPublic;
|
||||
}
|
||||
|
||||
public String getVendorName() {
|
||||
return vendorName;
|
||||
}
|
||||
|
||||
public Map<String, String> getDetails() {
|
||||
return convertDetailsToMap(details);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
|
||||
ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
try {
|
||||
HSMProfile profile = kmsManager.addHSMProfile(this);
|
||||
HSMProfileResponse response = kmsManager.createHSMProfileResponse(profile);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (KMSException e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true);
|
||||
if (accountId != null) {
|
||||
return accountId;
|
||||
}
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.HsmProfile;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// 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.user.kms.hsm;
|
||||
|
||||
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.ApiCommandResourceType;
|
||||
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.HSMProfileResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.kms.HSMProfile;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "deleteHSMProfile", description = "Deletes an HSM profile", responseObject = SuccessResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0",
|
||||
authorized = { RoleType.Admin })
|
||||
public class DeleteHSMProfileCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HSMProfileResponse.class, required = true,
|
||||
description = "the ID of the HSM profile")
|
||||
private Long id;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
|
||||
ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
try {
|
||||
boolean result = kmsManager.deleteHSMProfile(this);
|
||||
if (result) {
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
setResponseObject(response);
|
||||
} else {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete HSM profile");
|
||||
}
|
||||
} catch (KMSException e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
HSMProfile profile = _entityMgr.findById(HSMProfile.class, id);
|
||||
if (profile != null && profile.getAccountId() > 0) {
|
||||
return profile.getAccountId();
|
||||
}
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.HsmProfile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// 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.user.kms.hsm;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.HSMProfileResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "listHSMProfiles", description = "Lists HSM profiles", responseObject = HSMProfileResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class ListHSMProfilesCmd extends BaseListProjectAndAccountResourcesCmd {
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HSMProfileResponse.class,
|
||||
description = "the HSM profile ID")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class,
|
||||
description = "the zone ID")
|
||||
private Long zoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "the protocol of the HSM profile")
|
||||
private String protocol;
|
||||
|
||||
@Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, description = "list only enabled profiles")
|
||||
private Boolean enabled;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_SYSTEM,
|
||||
type = CommandType.BOOLEAN,
|
||||
description = "when true, non-admin users see only system (global) profiles")
|
||||
private Boolean isSystem;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public Boolean getIsSystem() {
|
||||
return isSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ListResponse<HSMProfileResponse> response = kmsManager.listHSMProfiles(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
// 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.user.kms.hsm;
|
||||
|
||||
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.ApiCommandResourceType;
|
||||
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.HSMProfileResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.kms.HSMProfile;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@APICommand(name = "updateHSMProfile", description = "Updates an HSM profile",
|
||||
responseObject = HSMProfileResponse.class,
|
||||
requestHasSensitiveInfo = true, responseHasSensitiveInfo = true, since = "4.23.0",
|
||||
authorized = { RoleType.Admin })
|
||||
public class UpdateHSMProfileCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HSMProfileResponse.class, required = true,
|
||||
description = "the ID of the HSM profile")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the HSM profile")
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN,
|
||||
description = "whether the HSM profile is enabled")
|
||||
private Boolean enabled;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException,
|
||||
ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
try {
|
||||
HSMProfile profile = kmsManager.updateHSMProfile(this);
|
||||
HSMProfileResponse response = kmsManager.createHSMProfileResponse(profile);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (KMSException e) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
HSMProfile profile = _entityMgr.findById(HSMProfile.class, id);
|
||||
if (profile != null && profile.getAccountId() > 0) {
|
||||
return profile.getAccountId();
|
||||
}
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.HsmProfile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -40,12 +40,14 @@ import org.apache.cloudstack.api.command.user.UserCmd;
|
|||
import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.HostResponse;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.api.response.NetworkResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
||||
import org.apache.cloudstack.api.response.UserDataResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.kms.KMSKey;
|
||||
import org.apache.cloudstack.vm.lease.VMLeaseManager;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
|
|
@ -126,11 +128,19 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme
|
|||
since = "4.4")
|
||||
private Long rootdisksize;
|
||||
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.ROOT_DISK_KMS_KEY_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = KMSKeyResponse.class,
|
||||
description = "ID of the KMS Key to use for root disk encryption",
|
||||
since = "4.23.0")
|
||||
private Long rootDiskKmsKeyId;
|
||||
|
||||
@Parameter(name = ApiConstants.DATADISKS_DETAILS,
|
||||
type = CommandType.MAP,
|
||||
since = "4.21.0",
|
||||
description = "Disk offering details for creating multiple data volumes. Mutually exclusive with diskOfferingId." +
|
||||
" Example: datadisksdetails[0].diskofferingid=a2a73a84-19db-4852-8930-dfddef053341&datadisksdetails[0].size=10&datadisksdetails[0].miniops=100&datadisksdetails[0].maxiops=200")
|
||||
" Example: datadisksdetails[0].diskofferingid=a2a73a84-19db-4852-8930-dfddef053341&datadisksdetails[0].size=10&datadisksdetails[0].miniops=100&datadisksdetails[0].maxiops=200&datadisksdetails[0].kmskeyid=<uuid>")
|
||||
private Map dataDisksDetails;
|
||||
|
||||
@Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine")
|
||||
|
|
@ -300,6 +310,10 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme
|
|||
return diskOfferingId;
|
||||
}
|
||||
|
||||
public Long getRootDiskKmsKeyId() {
|
||||
return rootDiskKmsKeyId;
|
||||
}
|
||||
|
||||
public String getDeploymentPlanner() {
|
||||
return deploymentPlanner;
|
||||
}
|
||||
|
|
@ -581,7 +595,19 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme
|
|||
minIops = Long.parseLong(dataDisk.get(ApiConstants.MIN_IOPS));
|
||||
maxIops = Long.parseLong(dataDisk.get(ApiConstants.MAX_IOPS));
|
||||
}
|
||||
VmDiskInfo vmDiskInfo = new VmDiskInfo(diskOffering, size, minIops, maxIops, deviceId);
|
||||
|
||||
// Extract KMS key ID if provided
|
||||
Long kmsKeyId = null;
|
||||
String kmsKeyUuid = dataDisk.get(ApiConstants.KMS_KEY_ID);
|
||||
if (kmsKeyUuid != null) {
|
||||
KMSKey kmsKey = _entityMgr.findByUuid(org.apache.cloudstack.kms.KMSKey.class, kmsKeyUuid);
|
||||
if (kmsKey == null) {
|
||||
throw new InvalidParameterValueException("Unable to find KMS key " + kmsKeyUuid);
|
||||
}
|
||||
kmsKeyId = kmsKey.getId();
|
||||
}
|
||||
|
||||
VmDiskInfo vmDiskInfo = new VmDiskInfo(diskOffering, size, minIops, maxIops, deviceId, kmsKeyId);
|
||||
vmDiskInfoList.add(vmDiskInfo);
|
||||
}
|
||||
this.dataDiskInfoList = vmDiskInfoList;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import org.apache.cloudstack.api.ServerApiException;
|
|||
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||
import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
|
|
@ -110,6 +111,13 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd implements UserC
|
|||
description = "The ID of the Instance; to be used with snapshot Id, Instance to which the volume gets attached after creation")
|
||||
private Long virtualMachineId;
|
||||
|
||||
@Parameter(name = ApiConstants.KMS_KEY_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = KMSKeyResponse.class,
|
||||
description = "ID of the KMS Key for volume encryption (required if encryption enabled for zone)",
|
||||
since = "4.23.0")
|
||||
private Long kmsKeyId;
|
||||
|
||||
@Parameter(name = ApiConstants.STORAGE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = StoragePoolResponse.class,
|
||||
|
|
@ -184,6 +192,10 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd implements UserC
|
|||
return virtualMachineId;
|
||||
}
|
||||
|
||||
public Long getKmsKeyId() {
|
||||
return kmsKeyId;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.apache.cloudstack.api.command.user.UserCmd;
|
|||
import org.apache.cloudstack.api.response.ClusterResponse;
|
||||
import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.HostResponse;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.PodResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
|
|
@ -90,6 +91,9 @@ public class ListVolumesCmd extends BaseListRetrieveOnlyResourceCountCmd impleme
|
|||
@Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "List volumes by disk offering", since = "4.4")
|
||||
private Long diskOfferingId;
|
||||
|
||||
@Parameter(name = ApiConstants.KMS_KEY_ID, type = CommandType.UUID, entityType = KMSKeyResponse.class, description = "List volumes by KMS Key", since = "4.23")
|
||||
private Long kmsKeyId;
|
||||
|
||||
@Parameter(name = ApiConstants.DISPLAY_VOLUME, type = CommandType.BOOLEAN, description = "List resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {
|
||||
RoleType.Admin})
|
||||
private Boolean display;
|
||||
|
|
@ -136,6 +140,10 @@ public class ListVolumesCmd extends BaseListRetrieveOnlyResourceCountCmd impleme
|
|||
return diskOfferingId;
|
||||
}
|
||||
|
||||
public Long getKmsKeyId() {
|
||||
return kmsKeyId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,182 @@
|
|||
// 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 java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.api.EntityReference;
|
||||
import org.apache.cloudstack.kms.HSMProfile;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@EntityReference(value = HSMProfile.class)
|
||||
public class HSMProfileResponse extends BaseResponse implements ControlledViewEntityResponse {
|
||||
@SerializedName(ApiConstants.ID)
|
||||
@Param(description = "the ID of the HSM profile")
|
||||
private String id;
|
||||
|
||||
@SerializedName(ApiConstants.NAME)
|
||||
@Param(description = "the name of the HSM profile")
|
||||
private String name;
|
||||
|
||||
@SerializedName(ApiConstants.PROTOCOL)
|
||||
@Param(description = "the protocol of the HSM profile")
|
||||
private String protocol;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT_ID)
|
||||
@Param(description = "the account ID of the HSM profile owner")
|
||||
private String accountId;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT)
|
||||
@Param(description = "the account name of the HSM profile owner")
|
||||
private String accountName;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN_ID)
|
||||
@Param(description = "the domain ID of the HSM profile owner")
|
||||
private String domainId;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN)
|
||||
@Param(description = "the domain name of the HSM profile owner")
|
||||
private String domainName;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN_PATH)
|
||||
@Param(description = "the domain path of the HSM profile owner")
|
||||
private String domainPath;
|
||||
|
||||
@SerializedName(ApiConstants.PROJECT_ID)
|
||||
@Param(description = "the project ID of the HSM profile owner")
|
||||
private String projectId;
|
||||
|
||||
@SerializedName(ApiConstants.PROJECT)
|
||||
@Param(description = "the project name of the HSM profile owner")
|
||||
private String projectName;
|
||||
|
||||
@SerializedName(ApiConstants.ZONE_ID)
|
||||
@Param(description = "the zone ID where the HSM profile is available")
|
||||
private String zoneId;
|
||||
|
||||
@SerializedName(ApiConstants.ZONE_NAME)
|
||||
@Param(description = "the zone name where the HSM profile is available")
|
||||
private String zoneName;
|
||||
|
||||
@SerializedName("vendor")
|
||||
@Param(description = "the vendor name of the HSM profile")
|
||||
private String vendorName;
|
||||
|
||||
@SerializedName(ApiConstants.STATE)
|
||||
@Param(description = "the state of the HSM profile")
|
||||
private String state;
|
||||
|
||||
@SerializedName(ApiConstants.ENABLED)
|
||||
@Param(description = "whether the HSM profile is enabled")
|
||||
private Boolean enabled;
|
||||
|
||||
@SerializedName(ApiConstants.IS_PUBLIC)
|
||||
@Param(description = "whether this is a system HSM profile available to all users globally")
|
||||
private Boolean isPublic;
|
||||
|
||||
@SerializedName(ApiConstants.CREATED)
|
||||
@Param(description = "the date the HSM profile was created")
|
||||
private Date created;
|
||||
|
||||
@SerializedName(ApiConstants.DETAILS)
|
||||
@Param(description = "HSM configuration details (sensitive values are encrypted)")
|
||||
private Map<String, String> details;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public void setAccountId(String accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccountName(String accountName) {
|
||||
this.accountName = accountName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDomainId(String domainId) {
|
||||
this.domainId = domainId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDomainName(String domainName) {
|
||||
this.domainName = domainName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDomainPath(String domainPath) {
|
||||
this.domainPath = domainPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProjectId(String projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public void setZoneId(String zoneId) {
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
public void setZoneName(String zoneName) {
|
||||
this.zoneName = zoneName;
|
||||
}
|
||||
|
||||
public void setVendorName(String vendorName) {
|
||||
this.vendorName = vendorName;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setIsPublic(Boolean isPublic) {
|
||||
this.isPublic = isPublic;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public void setDetails(Map<String, String> details) {
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* 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.acl.RoleType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.api.EntityReference;
|
||||
import org.apache.cloudstack.kms.KMSKey;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@EntityReference(value = KMSKey.class)
|
||||
public class KMSKeyResponse extends BaseResponse implements ControlledViewEntityResponse {
|
||||
|
||||
@SerializedName(ApiConstants.ID)
|
||||
@Param(description = "the UUID of the key")
|
||||
private String id;
|
||||
|
||||
@SerializedName(ApiConstants.NAME)
|
||||
@Param(description = "the name of the key")
|
||||
private String name;
|
||||
|
||||
@SerializedName(ApiConstants.DESCRIPTION)
|
||||
@Param(description = "the description of the key")
|
||||
private String description;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT)
|
||||
@Param(description = "the account owning the key")
|
||||
private String accountName;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT_ID)
|
||||
@Param(description = "the account ID owning the key")
|
||||
private String accountId;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN_ID)
|
||||
@Param(description = "the domain ID of the key")
|
||||
private String domainId;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN)
|
||||
@Param(description = "the domain name of the key")
|
||||
private String domainName;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN_PATH)
|
||||
@Param(description = "the domain path of the key")
|
||||
private String domainPath;
|
||||
|
||||
@SerializedName(ApiConstants.ZONE_ID)
|
||||
@Param(description = "the zone ID where the key is valid")
|
||||
private String zoneId;
|
||||
|
||||
@SerializedName(ApiConstants.ZONE_NAME)
|
||||
@Param(description = "the zone name where the key is valid")
|
||||
private String zoneName;
|
||||
|
||||
@SerializedName(ApiConstants.HSM_PROFILE_ID)
|
||||
@Param(description = "the zone ID where the key is valid")
|
||||
private String hsmProfileId;
|
||||
|
||||
@SerializedName(ApiConstants.HSM_PROFILE)
|
||||
@Param(description = "the zone name where the key is valid")
|
||||
private String hsmProfileName;
|
||||
|
||||
@SerializedName(ApiConstants.ALGORITHM)
|
||||
@Param(description = "the encryption algorithm")
|
||||
private String algorithm;
|
||||
|
||||
@SerializedName(ApiConstants.KEY_BITS)
|
||||
@Param(description = "the key size in bits")
|
||||
private Integer keyBits;
|
||||
|
||||
@SerializedName(ApiConstants.VERSION)
|
||||
@Param(description = "the key size in bits")
|
||||
private Integer version;
|
||||
|
||||
@SerializedName(ApiConstants.ENABLED)
|
||||
@Param(description = "whether the key is enabled")
|
||||
private Boolean enabled;
|
||||
|
||||
@SerializedName(ApiConstants.CREATED)
|
||||
@Param(description = "the creation timestamp")
|
||||
private Date created;
|
||||
|
||||
@SerializedName(ApiConstants.PROJECT_ID)
|
||||
@Param(description = "the project ID of the key")
|
||||
private String projectId;
|
||||
|
||||
@SerializedName(ApiConstants.PROJECT)
|
||||
@Param(description = "the project name of the key")
|
||||
private String projectName;
|
||||
|
||||
@SerializedName(ApiConstants.KEK_LABEL)
|
||||
@Param(description = "the provider-specific KEK label (admin only)", authorized = { RoleType.Admin })
|
||||
private String kekLabel;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccountName(String accountName) {
|
||||
this.accountName = accountName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProjectId(String projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public String getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setAccountId(String accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public String getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDomainId(String domainId) {
|
||||
this.domainId = domainId;
|
||||
}
|
||||
|
||||
public String getDomainName() {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDomainName(String domainName) {
|
||||
this.domainName = domainName;
|
||||
}
|
||||
|
||||
public String getDomainPath() {
|
||||
return domainPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDomainPath(String domainPath) {
|
||||
this.domainPath = domainPath;
|
||||
}
|
||||
|
||||
public String getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public void setZoneId(String zoneId) {
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
public String getZoneName() {
|
||||
return zoneName;
|
||||
}
|
||||
|
||||
public void setZoneName(String zoneName) {
|
||||
this.zoneName = zoneName;
|
||||
}
|
||||
|
||||
public String getHsmProfileId() {
|
||||
return hsmProfileId;
|
||||
}
|
||||
|
||||
public void setHsmProfileId(String hsmProfileId) {
|
||||
this.hsmProfileId = hsmProfileId;
|
||||
}
|
||||
|
||||
public String getHsmProfileName() {
|
||||
return hsmProfileName;
|
||||
}
|
||||
|
||||
public void setHsmProfileName(String hsmProfileName) {
|
||||
this.hsmProfileName = hsmProfileName;
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public void setAlgorithm(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public Integer getKeyBits() {
|
||||
return keyBits;
|
||||
}
|
||||
|
||||
public void setKeyBits(Integer keyBits) {
|
||||
this.keyBits = keyBits;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public String getKekLabel() {
|
||||
return kekLabel;
|
||||
}
|
||||
|
||||
public void setKekLabel(String kekLabel) {
|
||||
this.kekLabel = kekLabel;
|
||||
}
|
||||
}
|
||||
|
|
@ -309,6 +309,18 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co
|
|||
@Param(description = "the format of the disk encryption if applicable", since = "4.19.1")
|
||||
private String encryptionFormat;
|
||||
|
||||
@SerializedName(ApiConstants.KMS_KEY)
|
||||
@Param(description = "KMS key name of the volume", since = "4.23.0")
|
||||
private String kmsKey;
|
||||
|
||||
@SerializedName(ApiConstants.KMS_KEY_ID)
|
||||
@Param(description = "KMS key id of the volume", since = "4.23.0")
|
||||
private String kmsKeyId;
|
||||
|
||||
@SerializedName(ApiConstants.KMS_KEY_VERSION)
|
||||
@Param(description = "Version number of the KMS key used for disk encryption if applicable", since = "4.23.0")
|
||||
private Integer kmsKeyVersion;
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
|
@ -871,4 +883,28 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co
|
|||
public void setEncryptionFormat(String encryptionFormat) {
|
||||
this.encryptionFormat = encryptionFormat;
|
||||
}
|
||||
|
||||
public String getKmsKey() {
|
||||
return kmsKey;
|
||||
}
|
||||
|
||||
public void setKmsKey(String kmsKey) {
|
||||
this.kmsKey = kmsKey;
|
||||
}
|
||||
|
||||
public String getKmsKeyId() {
|
||||
return kmsKeyId;
|
||||
}
|
||||
|
||||
public void setKmsKeyId(String kmsKeyId) {
|
||||
this.kmsKeyId = kmsKeyId;
|
||||
}
|
||||
|
||||
public Integer getKmsKeyVersion() {
|
||||
return kmsKeyVersion;
|
||||
}
|
||||
|
||||
public void setKmsKeyVersion(Integer kmsKeyVersion) {
|
||||
this.kmsKeyVersion = kmsKeyVersion;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// 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.kms;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface HSMProfile extends Identity, InternalIdentity, ControlledEntity {
|
||||
String getName();
|
||||
|
||||
String getProtocol();
|
||||
|
||||
long getAccountId();
|
||||
|
||||
long getDomainId();
|
||||
|
||||
Long getZoneId();
|
||||
|
||||
String getVendorName();
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
boolean getIsPublic();
|
||||
|
||||
Date getCreated();
|
||||
|
||||
Date getRemoved();
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.kms;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.framework.kms.KeyPurpose;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* KMS Key (Key Encryption Key) metadata.
|
||||
* Represents a KEK that can be used to wrap/unwrap Data Encryption Keys (DEKs).
|
||||
* KEKs are account-scoped and used for envelope encryption.
|
||||
*/
|
||||
public interface KMSKey extends Identity, InternalIdentity, ControlledEntity {
|
||||
|
||||
String getName();
|
||||
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Provider-specific KEK label/ID (internal identifier used by the KMS provider)
|
||||
*/
|
||||
String getKekLabel();
|
||||
|
||||
KeyPurpose getPurpose();
|
||||
|
||||
Long getZoneId();
|
||||
|
||||
String getAlgorithm();
|
||||
|
||||
Integer getKeyBits();
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
Date getCreated();
|
||||
|
||||
Date getRemoved();
|
||||
|
||||
Long getHsmProfileId();
|
||||
}
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
// 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.kms;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.component.Manager;
|
||||
import org.apache.cloudstack.api.command.admin.kms.MigrateVolumesToKMSCmd;
|
||||
import org.apache.cloudstack.api.command.user.kms.RotateKMSKeyCmd;
|
||||
import org.apache.cloudstack.api.command.user.kms.CreateKMSKeyCmd;
|
||||
import org.apache.cloudstack.api.command.user.kms.DeleteKMSKeyCmd;
|
||||
import org.apache.cloudstack.api.command.user.kms.ListKMSKeysCmd;
|
||||
import org.apache.cloudstack.api.command.user.kms.UpdateKMSKeyCmd;
|
||||
import org.apache.cloudstack.api.command.user.kms.hsm.CreateHSMProfileCmd;
|
||||
import org.apache.cloudstack.api.command.user.kms.hsm.DeleteHSMProfileCmd;
|
||||
import org.apache.cloudstack.api.command.user.kms.hsm.ListHSMProfilesCmd;
|
||||
import org.apache.cloudstack.api.command.user.kms.hsm.UpdateHSMProfileCmd;
|
||||
import org.apache.cloudstack.api.response.HSMProfileResponse;
|
||||
import org.apache.cloudstack.api.response.KMSKeyResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.framework.kms.KMSProvider;
|
||||
import org.apache.cloudstack.framework.kms.WrappedKey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface KMSManager extends Manager, Configurable {
|
||||
|
||||
ConfigKey<Integer> KMSDekSizeBits = new ConfigKey<>(
|
||||
"Advanced",
|
||||
Integer.class,
|
||||
"kms.dek.size.bits",
|
||||
"256",
|
||||
"The size of Data Encryption Keys (DEK) in bits (128, 192, or 256)",
|
||||
true,
|
||||
ConfigKey.Scope.Global
|
||||
);
|
||||
|
||||
ConfigKey<Integer> KMSRetryCount = new ConfigKey<>(
|
||||
"Advanced",
|
||||
Integer.class,
|
||||
"kms.retry.count",
|
||||
"3",
|
||||
"Number of retry attempts for transient KMS failures",
|
||||
true,
|
||||
ConfigKey.Scope.Global
|
||||
);
|
||||
|
||||
ConfigKey<Integer> KMSRetryDelayMs = new ConfigKey<>(
|
||||
"Advanced",
|
||||
Integer.class,
|
||||
"kms.retry.delay.ms",
|
||||
"1000",
|
||||
"Delay in milliseconds between KMS retry attempts (exponential backoff)",
|
||||
true,
|
||||
ConfigKey.Scope.Global
|
||||
);
|
||||
|
||||
ConfigKey<Integer> KMSOperationTimeoutSec = new ConfigKey<>(
|
||||
"Advanced",
|
||||
Integer.class,
|
||||
"kms.operation.timeout.sec",
|
||||
"30",
|
||||
"Timeout in seconds for KMS cryptographic operations",
|
||||
true,
|
||||
ConfigKey.Scope.Global
|
||||
);
|
||||
|
||||
ConfigKey<Integer> KMSRewrapBatchSize = new ConfigKey<>(
|
||||
"Advanced",
|
||||
Integer.class,
|
||||
"kms.rewrap.batch.size",
|
||||
"50",
|
||||
"Number of wrapped keys to rewrap per batch in background job",
|
||||
true,
|
||||
ConfigKey.Scope.Global
|
||||
);
|
||||
|
||||
ConfigKey<Long> KMSRewrapIntervalMs = new ConfigKey<>(
|
||||
"Advanced",
|
||||
Long.class,
|
||||
"kms.rewrap.interval.ms",
|
||||
"300000",
|
||||
"Interval in milliseconds between background rewrap job executions (default: 5 minutes)",
|
||||
true,
|
||||
ConfigKey.Scope.Global
|
||||
);
|
||||
|
||||
ConfigKey<Integer> KMSOperationPoolCoreSize = new ConfigKey<>(
|
||||
"Advanced",
|
||||
Integer.class,
|
||||
"kms.operation.pool.core.size",
|
||||
"2",
|
||||
"Minimum number of threads kept alive for KMS cryptographic operations",
|
||||
true,
|
||||
ConfigKey.Scope.Global
|
||||
);
|
||||
|
||||
ConfigKey<Integer> KMSOperationPoolMaxSize = new ConfigKey<>(
|
||||
"Advanced",
|
||||
Integer.class,
|
||||
"kms.operation.pool.max.size",
|
||||
"100",
|
||||
"Maximum number of concurrent threads for KMS cryptographic operations. " +
|
||||
"Set this to match the concurrency limit of your HSM appliance or external KMS provider.",
|
||||
true,
|
||||
ConfigKey.Scope.Global
|
||||
);
|
||||
|
||||
/**
|
||||
* List all registered KMS providers
|
||||
*
|
||||
* @return list of available providers
|
||||
*/
|
||||
List<? extends KMSProvider> listKMSProviders();
|
||||
|
||||
/**
|
||||
* Get a specific KMS provider by name
|
||||
*
|
||||
* @param name provider name
|
||||
* @return the provider, or null if not found
|
||||
*/
|
||||
KMSProvider getKMSProvider(String name);
|
||||
|
||||
/**
|
||||
* Check if caller has permission to use a KMS key
|
||||
*
|
||||
* @param callerAccountId the caller's account ID
|
||||
* @param key the KMS key
|
||||
* @return true if caller has permission
|
||||
*/
|
||||
boolean hasPermission(Long callerAccountId, KMSKey key);
|
||||
|
||||
/**
|
||||
* Validates that the KMS key can be used for volume encryption: key exists, not deleted,
|
||||
* caller has access, key state is Enabled, and key purpose is VOLUME_ENCRYPTION.
|
||||
* No-op if kmsKeyId is null.
|
||||
*
|
||||
* @param caller the caller's account
|
||||
* @param kmsKeyId the KMS key database ID
|
||||
* @param zoneId the zone ID of the target resource (volume/VM)
|
||||
* @throws InvalidParameterValueException if key not found, deleted, disabled, wrong purpose, or zone mismatch
|
||||
* @throws PermissionDeniedException if caller lacks access
|
||||
*/
|
||||
void checkKmsKeyForVolumeEncryption(Account caller, Long kmsKeyId, Long zoneId);
|
||||
|
||||
/**
|
||||
* Unwrap a DEK by wrapped key ID, trying multiple KEK versions if needed
|
||||
*
|
||||
* @param wrappedKeyId the wrapped key database ID
|
||||
* @return plaintext DEK (caller must zeroize!)
|
||||
* @throws KMSException if unwrap fails
|
||||
*/
|
||||
byte[] unwrapKey(Long wrappedKeyId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Generate and wrap a DEK using a specific KMS key UUID
|
||||
*
|
||||
* @param kmsKey the KMS key
|
||||
* @param callerAccountId the caller's account ID
|
||||
* @return wrapped key ready for database storage
|
||||
* @throws KMSException if operation fails
|
||||
*/
|
||||
WrappedKey generateVolumeKeyWithKek(KMSKey kmsKey, Long callerAccountId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Create a KMS key and return the response object.
|
||||
* Handles validation, account resolution, and permission checks.
|
||||
*
|
||||
* @param cmd the create command with all parameters
|
||||
* @return KMSKeyResponse
|
||||
* @throws KMSException if creation fails
|
||||
*/
|
||||
KMSKeyResponse createKMSKey(CreateKMSKeyCmd cmd) throws KMSException;
|
||||
|
||||
/**
|
||||
* List KMS keys and return the response object.
|
||||
* Handles validation and permission checks.
|
||||
*
|
||||
* @param cmd the list command with all parameters
|
||||
* @return ListResponse with KMSKeyResponse objects
|
||||
*/
|
||||
ListResponse<KMSKeyResponse> listKMSKeys(ListKMSKeysCmd cmd);
|
||||
|
||||
/**
|
||||
* Update a KMS key and return the response object.
|
||||
* Handles validation and permission checks.
|
||||
*
|
||||
* @param cmd the update command with all parameters
|
||||
* @return KMSKeyResponse
|
||||
* @throws KMSException if update fails
|
||||
*/
|
||||
KMSKeyResponse updateKMSKey(UpdateKMSKeyCmd cmd) throws KMSException;
|
||||
|
||||
/**
|
||||
* Delete a KMS key and return the response object.
|
||||
* Handles validation and permission checks.
|
||||
*
|
||||
* @param cmd the delete command with all parameters
|
||||
* @return SuccessResponse
|
||||
* @throws KMSException if deletion fails
|
||||
*/
|
||||
SuccessResponse deleteKMSKey(DeleteKMSKeyCmd cmd) throws KMSException;
|
||||
|
||||
/**
|
||||
* Rotate KEK by creating new version and scheduling gradual re-encryption
|
||||
*
|
||||
* @param cmd the rotate command with all parameters
|
||||
* @return New KEK version UUID
|
||||
* @throws KMSException if rotation fails
|
||||
*/
|
||||
String rotateKMSKey(RotateKMSKeyCmd cmd) throws KMSException;
|
||||
|
||||
/**
|
||||
* Migrate passphrase-based volumes to KMS encryption
|
||||
*
|
||||
* @param cmd the migrate command with all parameters
|
||||
* @return Number of volumes successfully migrated
|
||||
* @throws KMSException if migration fails
|
||||
*/
|
||||
int migrateVolumesToKMS(MigrateVolumesToKMSCmd cmd) throws KMSException;
|
||||
|
||||
/**
|
||||
* Delete all KMS keys owned by an account (called during account cleanup)
|
||||
*
|
||||
* @param accountId the account ID
|
||||
* @return true if all keys were successfully deleted
|
||||
*/
|
||||
boolean deleteKMSKeysByAccountId(Long accountId);
|
||||
|
||||
/**
|
||||
* Add a new HSM profile
|
||||
*
|
||||
* @param cmd the add command
|
||||
* @return the created HSM profile
|
||||
* @throws KMSException if addition fails
|
||||
*/
|
||||
HSMProfile addHSMProfile(CreateHSMProfileCmd cmd) throws KMSException;
|
||||
|
||||
/**
|
||||
* List HSM profiles
|
||||
*
|
||||
* @param cmd the list command
|
||||
* @return list of HSM profiles
|
||||
*/
|
||||
ListResponse<HSMProfileResponse> listHSMProfiles(ListHSMProfilesCmd cmd);
|
||||
|
||||
/**
|
||||
* Delete an HSM profile
|
||||
*
|
||||
* @param cmd the delete command
|
||||
* @return true if deletion was successful
|
||||
* @throws KMSException if deletion fails
|
||||
*/
|
||||
boolean deleteHSMProfile(DeleteHSMProfileCmd cmd) throws KMSException;
|
||||
|
||||
/**
|
||||
* Update an HSM profile
|
||||
*
|
||||
* @param cmd the update command
|
||||
* @return the updated HSM profile
|
||||
* @throws KMSException if update fails
|
||||
*/
|
||||
HSMProfile updateHSMProfile(UpdateHSMProfileCmd cmd) throws KMSException;
|
||||
|
||||
/**
|
||||
* Create a response object for an HSM profile
|
||||
*
|
||||
* @param profile the HSM profile
|
||||
* @return the response object
|
||||
*/
|
||||
HSMProfileResponse createHSMProfileResponse(HSMProfile profile);
|
||||
}
|
||||
|
|
@ -256,6 +256,16 @@
|
|||
<artifactId>cloud-plugin-metrics</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-plugin-kms-database</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-plugin-kms-pkcs11</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-plugin-network-nvp</artifactId>
|
||||
|
|
|
|||
|
|
@ -366,4 +366,7 @@
|
|||
|
||||
<bean id="sharedFSProvidersRegistry" class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||
</bean>
|
||||
|
||||
<bean id="kmsProvidersRegistry" class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||
</bean>
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
name=kms
|
||||
parent=core
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
|
||||
>
|
||||
<bean class="org.apache.cloudstack.spring.lifecycle.registry.RegistryLifecycle">
|
||||
<property name="registry" ref="kmsProvidersRegistry" />
|
||||
<property name="typeClass" value="org.apache.cloudstack.framework.kms.KMSProvider" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
|
@ -120,7 +120,7 @@ public interface VolumeOrchestrationService {
|
|||
void destroyVolume(Volume volume);
|
||||
|
||||
DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template,
|
||||
Account owner, Long deviceId, boolean incrementResourceCount);
|
||||
Account owner, Long deviceId, Long kmsKeyId, boolean incrementResourceCount);
|
||||
|
||||
VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException;
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ public interface VolumeOrchestrationService {
|
|||
* Allocate a volume or multiple volumes in case of template is registered with the 'deploy-as-is' option, allowing multiple disks
|
||||
*/
|
||||
List<DiskProfile> allocateTemplatedVolumes(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm,
|
||||
Account owner, Volume volume, Snapshot snapshot);
|
||||
Account owner, Long kmsKeyId, Volume volume, Snapshot snapshot);
|
||||
|
||||
String getVmNameFromVolumeId(long volumeId);
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public interface OrchestrationService {
|
|||
@QueryParam("network-nic-map") Map<String, List<NicProfile>> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan,
|
||||
@QueryParam("root-disk-size") Long rootDiskSize, @QueryParam("extra-dhcp-option-map") Map<String, Map<Integer, String>> extraDhcpOptionMap,
|
||||
@QueryParam("datadisktemplate-diskoffering-map") Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap, @QueryParam("disk-offering-id") Long diskOfferingId,
|
||||
@QueryParam("root-disk-offering-id") Long rootDiskOfferingId, List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException;
|
||||
@QueryParam("root-disk-offering-id") Long rootDiskOfferingId, @QueryParam("root-disk-kms-key-id") Long rootDiskKmsKeyId, List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException;
|
||||
|
||||
@POST
|
||||
VirtualMachineEntity createVirtualMachineFromScratch(@QueryParam("id") String id, @QueryParam("owner") String owner, @QueryParam("iso-id") String isoId,
|
||||
|
|
@ -80,7 +80,7 @@ public interface OrchestrationService {
|
|||
@QueryParam("compute-tags") List<String> computeTags, @QueryParam("root-disk-tags") List<String> rootDiskTags,
|
||||
@QueryParam("network-nic-map") Map<String, List<NicProfile>> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan,
|
||||
@QueryParam("extra-dhcp-option-map") Map<String, Map<Integer, String>> extraDhcpOptionMap, @QueryParam("disk-offering-id") Long diskOfferingId,
|
||||
@QueryParam("data-disks-offering-info") List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException;
|
||||
@QueryParam("root-disk-kms-key-id") Long rootDiskKmsKeyId, @QueryParam("data-disks-offering-info") List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException;
|
||||
|
||||
@POST
|
||||
NetworkEntity createNetwork(String id, String name, String domainName, String cidr, String gateway);
|
||||
|
|
|
|||
|
|
@ -586,7 +586,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
Long deviceId = dataDiskDeviceIds.get(index++);
|
||||
String volumeName = deviceId == null ? "DATA-" + persistedVm.getId() : "DATA-" + persistedVm.getId() + "-" + String.valueOf(deviceId);
|
||||
volumeMgr.allocateRawVolume(Type.DATADISK, volumeName, dataDiskOfferingInfo.getDiskOffering(), dataDiskOfferingInfo.getSize(),
|
||||
dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, deviceId, true);
|
||||
dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, deviceId, dataDiskOfferingInfo.getKmsKeyId(), true);
|
||||
}
|
||||
}
|
||||
if (datadiskTemplateToDiskOfferingMap != null && !datadiskTemplateToDiskOfferingMap.isEmpty()) {
|
||||
|
|
@ -596,7 +596,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
long diskOfferingSize = diskOffering.getDiskSize() / (1024 * 1024 * 1024);
|
||||
VMTemplateVO dataDiskTemplate = _templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey());
|
||||
volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + persistedVm.getId() + "-" + String.valueOf( diskNumber), diskOffering, diskOfferingSize, null, null,
|
||||
persistedVm, dataDiskTemplate, owner, diskNumber, true);
|
||||
persistedVm, dataDiskTemplate, owner, diskNumber, null, true);
|
||||
diskNumber++;
|
||||
}
|
||||
}
|
||||
|
|
@ -626,12 +626,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
String rootVolumeName = String.format("ROOT-%s", vm.getId());
|
||||
if (template.getFormat() == ImageFormat.ISO) {
|
||||
volumeMgr.allocateRawVolume(Type.ROOT, rootVolumeName, rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(),
|
||||
rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vm, template, owner, null, true);
|
||||
rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vm, template, owner, null, rootDiskOfferingInfo.getKmsKeyId(), true);
|
||||
} else if (Arrays.asList(ImageFormat.BAREMETAL, ImageFormat.EXTERNAL).contains(template.getFormat())) {
|
||||
logger.debug("{} has format [{}]. Skipping ROOT volume [{}] allocation.", template, template.getFormat(), rootVolumeName);
|
||||
} else {
|
||||
volumeMgr.allocateTemplatedVolumes(Type.ROOT, rootVolumeName, rootDiskOfferingInfo.getDiskOffering(), rootDiskSizeFinal,
|
||||
rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), template, vm, owner, volume, snapshot);
|
||||
rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), template, vm, owner, rootDiskOfferingInfo.getKmsKeyId(), volume, snapshot);
|
||||
}
|
||||
} finally {
|
||||
// Remove volumeContext and pop vmContext back
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ public class CloudOrchestrator implements OrchestrationService {
|
|||
public VirtualMachineEntity createVirtualMachine(String id, String owner, String templateId, String hostName, String displayName, String hypervisor, int cpu,
|
||||
int speed, long memory, Long diskSize, List<String> computeTags, List<String> rootDiskTags, Map<String, List<NicProfile>> networkNicMap, DeploymentPlan plan,
|
||||
Long rootDiskSize, Map<String, Map<Integer, String>> extraDhcpOptionMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Long dataDiskOfferingId, Long rootDiskOfferingId,
|
||||
List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException {
|
||||
Long rootDiskKmsKeyId, List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException {
|
||||
|
||||
// VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks,
|
||||
// vmEntityManager);
|
||||
|
|
@ -198,6 +198,7 @@ public class CloudOrchestrator implements OrchestrationService {
|
|||
}
|
||||
rootDiskOfferingInfo.setDiskOffering(rootDiskOffering);
|
||||
rootDiskOfferingInfo.setSize(rootDiskSize);
|
||||
rootDiskOfferingInfo.setKmsKeyId(rootDiskKmsKeyId);
|
||||
|
||||
if (rootDiskOffering.isCustomizedIops() != null && rootDiskOffering.isCustomizedIops()) {
|
||||
Map<String, String> userVmDetails = _vmInstanceDetailsDao.listDetailsKeyPairs(vm.getId());
|
||||
|
|
@ -280,7 +281,7 @@ public class CloudOrchestrator implements OrchestrationService {
|
|||
@Override
|
||||
public VirtualMachineEntity createVirtualMachineFromScratch(String id, String owner, String isoId, String hostName, String displayName, String hypervisor, String os,
|
||||
int cpu, int speed, long memory, Long diskSize, List<String> computeTags, List<String> rootDiskTags, Map<String, List<NicProfile>> networkNicMap, DeploymentPlan plan,
|
||||
Map<String, Map<Integer, String>> extraDhcpOptionMap, Long diskOfferingId, List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot)
|
||||
Map<String, Map<Integer, String>> extraDhcpOptionMap, Long diskOfferingId, Long rootDiskKmsKeyId, List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot)
|
||||
throws InsufficientCapacityException {
|
||||
|
||||
// VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, vmEntityManager);
|
||||
|
|
@ -314,6 +315,7 @@ public class CloudOrchestrator implements OrchestrationService {
|
|||
|
||||
rootDiskOfferingInfo.setDiskOffering(diskOffering);
|
||||
rootDiskOfferingInfo.setSize(size);
|
||||
rootDiskOfferingInfo.setKmsKeyId(rootDiskKmsKeyId);
|
||||
|
||||
if (diskOffering.isCustomizedIops() != null && diskOffering.isCustomizedIops()) {
|
||||
Map<String, String> userVmDetails = _vmInstanceDetailsDao.listDetailsKeyPairs(vm.getId());
|
||||
|
|
|
|||
|
|
@ -87,6 +87,13 @@ import org.apache.cloudstack.resourcelimit.Reserver;
|
|||
import org.apache.cloudstack.secret.PassphraseVO;
|
||||
import org.apache.cloudstack.secret.dao.PassphraseDao;
|
||||
import org.apache.cloudstack.snapshot.SnapshotHelper;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
import org.apache.cloudstack.kms.KMSKeyVO;
|
||||
import org.apache.cloudstack.kms.KMSWrappedKeyVO;
|
||||
import org.apache.cloudstack.kms.dao.KMSKeyDao;
|
||||
import org.apache.cloudstack.kms.dao.KMSWrappedKeyDao;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.framework.kms.WrappedKey;
|
||||
import org.apache.cloudstack.storage.command.CommandResult;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
|
|
@ -281,6 +288,12 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
|
||||
@Inject
|
||||
private DataStoreProviderManager dataStoreProviderMgr;
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
@Inject
|
||||
private KMSKeyDao kmsKeyDao;
|
||||
@Inject
|
||||
private KMSWrappedKeyDao kmsWrappedKeyDao;
|
||||
|
||||
private final StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine;
|
||||
protected List<StoragePoolAllocator> _storagePoolAllocators;
|
||||
|
|
@ -509,7 +522,9 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId());
|
||||
if (diskOffering.getEncrypt()) {
|
||||
VolumeVO vol = (VolumeVO) volume;
|
||||
volume = setPassphraseForVolumeEncryption(vol);
|
||||
// Retrieve KMS key from volume's kmsKeyId if provided
|
||||
KMSKeyVO kmsKey = getKmsKeyFromVolume(vol);
|
||||
volume = setPassphraseForVolumeEncryption(vol, kmsKey, volume.getAccountId());
|
||||
}
|
||||
DataCenter dc = _entityMgr.findById(DataCenter.class, volume.getDataCenterId());
|
||||
DiskProfile dskCh = new DiskProfile(volume, diskOffering, snapshot.getHypervisorType());
|
||||
|
|
@ -726,7 +741,9 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
|
||||
if (diskOffering.getEncrypt()) {
|
||||
VolumeVO vol = _volsDao.findById(volumeInfo.getId());
|
||||
setPassphraseForVolumeEncryption(vol);
|
||||
// Retrieve KMS key from volume's kmsKeyId if provided
|
||||
KMSKeyVO kmsKey = getKmsKeyFromVolume(vol);
|
||||
setPassphraseForVolumeEncryption(vol, kmsKey, vol.getAccountId());
|
||||
volumeInfo = volFactory.getVolume(volumeInfo.getId());
|
||||
}
|
||||
}
|
||||
|
|
@ -863,8 +880,10 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true)
|
||||
@Override
|
||||
public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, Account owner,
|
||||
Long deviceId, boolean incrementResourceCount) {
|
||||
public DiskProfile allocateRawVolume(
|
||||
Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm,
|
||||
VirtualMachineTemplate template, Account owner, Long deviceId, Long kmsKeyId, boolean incrementResourceCount
|
||||
) {
|
||||
if (size == null) {
|
||||
size = offering.getDiskSize();
|
||||
} else {
|
||||
|
|
@ -897,6 +916,11 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
vol.setDisplayVolume(userVm.isDisplayVm());
|
||||
}
|
||||
|
||||
// Set KMS key ID if provided
|
||||
if (kmsKeyId != null) {
|
||||
vol.setKmsKeyId(kmsKeyId);
|
||||
}
|
||||
|
||||
vol.setFormat(getSupportedImageFormatForCluster(vm.getHypervisorType()));
|
||||
vol = _volsDao.persist(vol);
|
||||
|
||||
|
|
@ -916,7 +940,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
}
|
||||
|
||||
private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm,
|
||||
Account owner, long deviceId, String configurationId, Volume volume, Snapshot snapshot) {
|
||||
Account owner, long deviceId, String configurationId, Long kmsKeyId, Volume volume, Snapshot snapshot) {
|
||||
assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template.";
|
||||
|
||||
if (volume != null) {
|
||||
|
|
@ -966,6 +990,11 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
vol.setDisplayVolume(userVm.isDisplayVm());
|
||||
}
|
||||
|
||||
// Set KMS key ID if provided
|
||||
if (kmsKeyId != null) {
|
||||
vol.setKmsKeyId(kmsKeyId);
|
||||
}
|
||||
|
||||
vol = _volsDao.persist(vol);
|
||||
|
||||
saveVolumeDetails(offering.getId(), vol.getId());
|
||||
|
|
@ -1055,7 +1084,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating ROOT volume", create = true)
|
||||
@Override
|
||||
public List<DiskProfile> allocateTemplatedVolumes(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm,
|
||||
Account owner, Volume volume, Snapshot snapshot) {
|
||||
Account owner, Long kmsKeyId, Volume volume, Snapshot snapshot) {
|
||||
String templateToString = getReflectOnlySelectedFields(template);
|
||||
|
||||
int volumesNumber = 1;
|
||||
|
|
@ -1102,7 +1131,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
}
|
||||
logger.info("Adding disk object [{}] to VM [{}]", volumeName, vm);
|
||||
DiskProfile diskProfile = allocateTemplatedVolume(type, volumeName, offering, volumeSize, minIops, maxIops,
|
||||
template, vm, owner, deviceId, configurationId, volume, snapshot);
|
||||
template, vm, owner, deviceId, configurationId, kmsKeyId, volume, snapshot);
|
||||
profiles.add(diskProfile);
|
||||
}
|
||||
|
||||
|
|
@ -1777,7 +1806,9 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
if (vol.getState() == Volume.State.Allocated || vol.getState() == Volume.State.Creating) {
|
||||
DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, vol.getDiskOfferingId());
|
||||
if (diskOffering.getEncrypt()) {
|
||||
vol = setPassphraseForVolumeEncryption(vol);
|
||||
// Retrieve KMS key from volume's kmsKeyId if provided
|
||||
KMSKeyVO kmsKey = getKmsKeyFromVolume(vol);
|
||||
vol = setPassphraseForVolumeEncryption(vol, kmsKey, vol.getAccountId());
|
||||
}
|
||||
newVol = vol;
|
||||
} else {
|
||||
|
|
@ -1900,16 +1931,72 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
return new Pair<>(newVol, destPool);
|
||||
}
|
||||
|
||||
private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume) {
|
||||
if (volume.getPassphraseId() != null) {
|
||||
/**
|
||||
* Helper method to retrieve KMS key from volume's kmsKeyId
|
||||
*/
|
||||
private KMSKeyVO getKmsKeyFromVolume(VolumeVO volume) {
|
||||
if (volume.getKmsKeyId() == null) {
|
||||
return null;
|
||||
}
|
||||
return kmsKeyDao.findById(volume.getKmsKeyId());
|
||||
}
|
||||
|
||||
private VolumeVO setKmsKeyForVolumeEncryption(VolumeVO volume, KMSKeyVO kmsKey, Long callerAccountId) {
|
||||
// Determine caller account ID if not provided
|
||||
if (callerAccountId == null) {
|
||||
callerAccountId = volume.getAccountId();
|
||||
}
|
||||
|
||||
// Validate permission
|
||||
if (!kmsManager.hasPermission(callerAccountId, kmsKey)) {
|
||||
throw new CloudRuntimeException("No permission to use KMS key: " + kmsKey);
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug("Generating and wrapping DEK for volume {} using KMS key {}", volume.getName(), kmsKey.getUuid());
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// Generate and wrap DEK using active KEK version
|
||||
WrappedKey wrappedKey = kmsManager.generateVolumeKeyWithKek(kmsKey, callerAccountId);
|
||||
|
||||
// The wrapped key is already persisted by generateVolumeKeyWithKek, get its ID
|
||||
KMSWrappedKeyVO wrappedKeyVO = kmsWrappedKeyDao.findByUuid(wrappedKey.getUuid());
|
||||
if (wrappedKeyVO == null) {
|
||||
throw new CloudRuntimeException("Failed to find persisted wrapped key: " + wrappedKey.getUuid());
|
||||
}
|
||||
|
||||
// Set the wrapped key ID on the volume
|
||||
volume.setKmsWrappedKeyId(wrappedKeyVO.getId());
|
||||
|
||||
long finishTime = System.currentTimeMillis();
|
||||
logger.debug("Generating and persisting wrapped key took {} ms for volume: {}",
|
||||
(finishTime - startTime), volume.getName());
|
||||
|
||||
return _volsDao.persist(volume);
|
||||
|
||||
} catch (KMSException e) {
|
||||
throw new CloudRuntimeException("KMS failure while setting up volume encryption: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume, KMSKeyVO kmsKey, Long callerAccountId) {
|
||||
// If volume already has encryption set up, return it
|
||||
if (volume.getKmsWrappedKeyId() != null || volume.getPassphraseId() != null) {
|
||||
return volume;
|
||||
}
|
||||
logger.debug("Creating passphrase for the volume: " + volume.getName());
|
||||
if (kmsKey != null) {
|
||||
return setKmsKeyForVolumeEncryption(volume, kmsKey, callerAccountId);
|
||||
}
|
||||
// Legacy: passphrase-based encryption (fallback when KMS not enabled or KMS key not specified)
|
||||
return setPassphraseForVolumeEncryption(volume);
|
||||
}
|
||||
|
||||
private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume) {
|
||||
logger.debug("Creating passphrase for the volume: {}", volume.getName());
|
||||
long startTime = System.currentTimeMillis();
|
||||
PassphraseVO passphrase = passphraseDao.persist(new PassphraseVO(true));
|
||||
volume.setPassphraseId(passphrase.getId());
|
||||
long finishTime = System.currentTimeMillis();
|
||||
logger.debug("Creating and persisting passphrase took: " + (finishTime - startTime) + " ms for the volume: " + volume.toString());
|
||||
logger.debug("Creating and persisting passphrase took: {} ms for the volume: {}", finishTime - startTime, volume.toString());
|
||||
return _volsDao.persist(volume);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,11 @@
|
|||
<artifactId>cloud-framework-db</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-kms</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
|
|
|
|||
|
|
@ -182,6 +182,12 @@ public class VolumeVO implements Volume {
|
|||
@Column(name = "passphrase_id")
|
||||
private Long passphraseId;
|
||||
|
||||
@Column(name = "kms_key_id")
|
||||
private Long kmsKeyId;
|
||||
|
||||
@Column(name = "kms_wrapped_key_id")
|
||||
private Long kmsWrappedKeyId;
|
||||
|
||||
@Column(name = "encrypt_format")
|
||||
private String encryptFormat;
|
||||
|
||||
|
|
@ -683,6 +689,14 @@ public class VolumeVO implements Volume {
|
|||
|
||||
public void setPassphraseId(Long id) { this.passphraseId = id; }
|
||||
|
||||
public Long getKmsKeyId() { return kmsKeyId; }
|
||||
|
||||
public void setKmsKeyId(Long id) { this.kmsKeyId = id; }
|
||||
|
||||
public Long getKmsWrappedKeyId() { return kmsWrappedKeyId; }
|
||||
|
||||
public void setKmsWrappedKeyId(Long id) { this.kmsWrappedKeyId = id; }
|
||||
|
||||
public String getEncryptFormat() { return encryptFormat; }
|
||||
|
||||
public void setEncryptFormat(String encryptFormat) { this.encryptFormat = encryptFormat; }
|
||||
|
|
|
|||
|
|
@ -109,6 +109,17 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.S
|
|||
*/
|
||||
List<VolumeVO> listVolumesByPassphraseId(long passphraseId);
|
||||
|
||||
/**
|
||||
* List volumes with passphrase_id for migration to KMS
|
||||
*
|
||||
* @param zoneId Zone ID (required)
|
||||
* @param accountId Account ID filter (optional, null for all accounts)
|
||||
* @param domainId Domain ID filter (optional, null for all domains)
|
||||
* @param limit Maximum number of volumes to return
|
||||
* @return list of volumes that need migration
|
||||
*/
|
||||
Pair<List<VolumeVO>, Integer> listVolumesForKMSMigration(Long zoneId, Long accountId, Long domainId, Integer limit);
|
||||
|
||||
/**
|
||||
* Gets the Total Primary Storage space allocated for an account
|
||||
*
|
||||
|
|
@ -167,6 +178,8 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.S
|
|||
|
||||
VolumeVO findByLastIdAndState(long lastVolumeId, Volume.State...states);
|
||||
|
||||
boolean existsWithKmsKey(long kmsKeyId);
|
||||
|
||||
/**
|
||||
* Retrieves volume by its externalId
|
||||
*
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import javax.inject.Inject;
|
|||
|
||||
import org.apache.cloudstack.reservation.ReservationVO;
|
||||
import org.apache.cloudstack.reservation.dao.ReservationDao;
|
||||
import org.apache.cloudstack.kms.dao.KMSWrappedKeyDao;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
@ -80,11 +81,14 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
protected GenericSearchBuilder<VolumeVO, SumCount> secondaryStorageSearch;
|
||||
private final SearchBuilder<VolumeVO> poolAndPathSearch;
|
||||
final GenericSearchBuilder<VolumeVO, Integer> CountByOfferingId;
|
||||
private final SearchBuilder<VolumeVO> kmsMigrationSearch;
|
||||
|
||||
@Inject
|
||||
ReservationDao reservationDao;
|
||||
@Inject
|
||||
ResourceTagDao tagsDao;
|
||||
@Inject
|
||||
KMSWrappedKeyDao kmsWrappedKeyDao;
|
||||
|
||||
// need to account for zone-wide primary storage where storage_pool has
|
||||
// null-value pod and cluster, where hypervisor information is stored in
|
||||
|
|
@ -401,6 +405,7 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
AllFieldsSearch.and("passphraseId", AllFieldsSearch.entity().getPassphraseId(), Op.EQ);
|
||||
AllFieldsSearch.and("iScsiName", AllFieldsSearch.entity().get_iScsiName(), Op.EQ);
|
||||
AllFieldsSearch.and("path", AllFieldsSearch.entity().getPath(), Op.EQ);
|
||||
AllFieldsSearch.and("kmsKeyId", AllFieldsSearch.entity().getKmsKeyId(), Op.EQ);
|
||||
AllFieldsSearch.done();
|
||||
|
||||
RootDiskStateSearch = createSearchBuilder();
|
||||
|
|
@ -517,6 +522,13 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
CountByOfferingId.select(null, Func.COUNT, CountByOfferingId.entity().getId());
|
||||
CountByOfferingId.and("diskOfferingId", CountByOfferingId.entity().getDiskOfferingId(), Op.EQ);
|
||||
CountByOfferingId.done();
|
||||
|
||||
kmsMigrationSearch = createSearchBuilder();
|
||||
kmsMigrationSearch.and("passphraseId", kmsMigrationSearch.entity().getPassphraseId(), Op.NNULL);
|
||||
kmsMigrationSearch.and("zoneId", kmsMigrationSearch.entity().getDataCenterId(), Op.EQ);
|
||||
kmsMigrationSearch.and("accountId", kmsMigrationSearch.entity().getAccountId(), Op.EQ);
|
||||
kmsMigrationSearch.and("domainId", kmsMigrationSearch.entity().getDomainId(), Op.EQ);
|
||||
kmsMigrationSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -737,6 +749,21 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<VolumeVO>, Integer> listVolumesForKMSMigration(Long zoneId, Long accountId, Long domainId, Integer limit) {
|
||||
SearchCriteria<VolumeVO> sc = kmsMigrationSearch.create();
|
||||
|
||||
Filter filter = new Filter(limit);
|
||||
sc.setParameters("zoneId", zoneId);
|
||||
if (accountId != null) {
|
||||
sc.setParameters("accountId", accountId);
|
||||
}
|
||||
if (domainId != null) {
|
||||
sc.setParameters("domainId", domainId);
|
||||
}
|
||||
return searchAndCount(sc, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
public boolean remove(Long id) {
|
||||
|
|
@ -745,6 +772,17 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
logger.debug(String.format("Removing volume %s from DB", id));
|
||||
VolumeVO entry = findById(id);
|
||||
if (entry != null) {
|
||||
// Clean up KMS wrapped key if volume was encrypted with KMS
|
||||
if (entry.getKmsWrappedKeyId() != null) {
|
||||
try {
|
||||
kmsWrappedKeyDao.remove(entry.getKmsWrappedKeyId());
|
||||
logger.debug("Removed KMS wrapped key [id={}] for volume [id={}, uuid={}]",
|
||||
entry.getKmsWrappedKeyId(), id, entry.getUuid());
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to remove KMS wrapped key [id={}] for volume [id={}, uuid={}]: {}",
|
||||
entry.getKmsWrappedKeyId(), id, entry.getUuid(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
tagsDao.removeByIdAndType(id, ResourceObjectType.Volume);
|
||||
}
|
||||
boolean result = super.remove(id);
|
||||
|
|
@ -941,6 +979,13 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean existsWithKmsKey(long kmsKeyId) {
|
||||
SearchCriteria<VolumeVO> sc = AllFieldsSearch.create();
|
||||
sc.setParameters("kmsKeyId", kmsKeyId);
|
||||
sc.setParameters("notDestroyed", Volume.State.Expunged, Volume.State.Destroy);
|
||||
return findOneBy(sc) != null;
|
||||
}
|
||||
|
||||
public VolumeVO findByExternalUuid(String externalUuid) {
|
||||
SearchCriteria<VolumeVO> sc = ExternalUuidSearch.create();
|
||||
sc.setParameters("externalUuid", externalUuid);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
// 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.kms;
|
||||
|
||||
import org.apache.cloudstack.api.ResourceDetail;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "kms_hsm_profile_details")
|
||||
public class HSMProfileDetailsVO implements ResourceDetail {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private long id;
|
||||
|
||||
@Column(name = "profile_id")
|
||||
private long resourceId;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "value")
|
||||
private String value;
|
||||
|
||||
public HSMProfileDetailsVO() {
|
||||
}
|
||||
|
||||
public HSMProfileDetailsVO(long profileId, String name, String value) {
|
||||
this.resourceId = profileId;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisplay() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
// 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.kms;
|
||||
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "kms_hsm_profiles")
|
||||
public class HSMProfileVO implements HSMProfile {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private long id;
|
||||
|
||||
@Column(name = "uuid")
|
||||
private String uuid;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "protocol")
|
||||
private String protocol;
|
||||
|
||||
@Column(name = "account_id")
|
||||
private Long accountId;
|
||||
|
||||
@Column(name = "domain_id")
|
||||
private Long domainId;
|
||||
|
||||
@Column(name = "zone_id")
|
||||
private Long zoneId;
|
||||
|
||||
@Column(name = "vendor_name")
|
||||
private String vendorName;
|
||||
|
||||
@Column(name = "enabled")
|
||||
private boolean enabled;
|
||||
|
||||
@Column(name = "is_public")
|
||||
private boolean isPublic;
|
||||
|
||||
@Column(name = "created")
|
||||
private Date created;
|
||||
|
||||
@Column(name = "removed")
|
||||
private Date removed;
|
||||
|
||||
public HSMProfileVO() {
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
this.created = new Date();
|
||||
this.isPublic = false;
|
||||
}
|
||||
|
||||
public HSMProfileVO(String name, String protocol, Long accountId, Long domainId, Long zoneId, String vendorName) {
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
this.name = name;
|
||||
this.protocol = protocol;
|
||||
this.accountId = accountId;
|
||||
this.domainId = domainId;
|
||||
this.zoneId = zoneId;
|
||||
this.vendorName = vendorName;
|
||||
this.enabled = true;
|
||||
this.isPublic = false;
|
||||
this.created = new Date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("HSMProfileVO %s",
|
||||
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||
this, "id", "uuid", "name", "protocol", "system", "enabled"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAccountId() {
|
||||
return accountId == null ? -1 : accountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDomainId() {
|
||||
return domainId == null ? -1 : domainId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVendorName() {
|
||||
return vendorName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getEntityType() {
|
||||
return HSMProfile.class;
|
||||
}
|
||||
|
||||
public void setRemoved(Date removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setVendorName(String vendorName) {
|
||||
this.vendorName = vendorName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getIsPublic() {
|
||||
return isPublic;
|
||||
}
|
||||
|
||||
public void setIsPublic(boolean isPublic) {
|
||||
this.isPublic = isPublic;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
// 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.kms;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Database entity for KEK versions.
|
||||
* Tracks multiple KEK versions per KMS key to support gradual rotation.
|
||||
* During rotation, a new version is created (status=Active) and old versions
|
||||
* are marked as Previous (still usable for decryption) or Archived (no longer used).
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "kms_kek_versions")
|
||||
public class KMSKekVersionVO {
|
||||
|
||||
public enum Status {
|
||||
/**
|
||||
* Used for new encryption operations
|
||||
*/
|
||||
Active,
|
||||
/**
|
||||
* Still usable for decryption during key rotation
|
||||
*/
|
||||
Previous,
|
||||
/**
|
||||
* No longer used; all wrapped keys have been re-encrypted
|
||||
*/
|
||||
Archived
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "uuid", nullable = false)
|
||||
private String uuid;
|
||||
|
||||
@Column(name = "kms_key_id", nullable = false)
|
||||
private Long kmsKeyId;
|
||||
|
||||
@Column(name = "version_number", nullable = false)
|
||||
private Integer versionNumber;
|
||||
|
||||
@Column(name = "kek_label", nullable = false)
|
||||
private String kekLabel;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 32)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Status status;
|
||||
|
||||
@Column(name = "hsm_profile_id")
|
||||
private Long hsmProfileId;
|
||||
|
||||
@Column(name = GenericDao.CREATED_COLUMN, nullable = false)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date created;
|
||||
|
||||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date removed;
|
||||
|
||||
public KMSKekVersionVO(Long kmsKeyId, Integer versionNumber, String kekLabel) {
|
||||
this();
|
||||
this.kmsKeyId = kmsKeyId;
|
||||
this.versionNumber = versionNumber;
|
||||
this.kekLabel = kekLabel;
|
||||
this.status = Status.Active;
|
||||
}
|
||||
|
||||
public KMSKekVersionVO(Long kmsKeyId, String kekLabel) {
|
||||
this();
|
||||
this.kmsKeyId = kmsKeyId;
|
||||
this.kekLabel = kekLabel;
|
||||
this.status = Status.Active;
|
||||
}
|
||||
|
||||
public KMSKekVersionVO() {
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
this.created = new Date();
|
||||
this.status = Status.Active;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public Long getKmsKeyId() {
|
||||
return kmsKeyId;
|
||||
}
|
||||
|
||||
public void setKmsKeyId(Long kmsKeyId) {
|
||||
this.kmsKeyId = kmsKeyId;
|
||||
}
|
||||
|
||||
public Integer getVersionNumber() {
|
||||
return versionNumber;
|
||||
}
|
||||
|
||||
public void setVersionNumber(Integer versionNumber) {
|
||||
this.versionNumber = versionNumber;
|
||||
}
|
||||
|
||||
public String getKekLabel() {
|
||||
return kekLabel;
|
||||
}
|
||||
|
||||
public void setKekLabel(String kekLabel) {
|
||||
this.kekLabel = kekLabel;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Long getHsmProfileId() {
|
||||
return hsmProfileId;
|
||||
}
|
||||
|
||||
public void setHsmProfileId(Long hsmProfileId) {
|
||||
this.hsmProfileId = hsmProfileId;
|
||||
}
|
||||
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void setRemoved(Date removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("KMSKekVersion %s",
|
||||
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||
this, "id", "uuid", "kmsKeyId", "versionNumber", "status", "kekLabel"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
// 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.kms;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.framework.kms.KeyPurpose;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Database entity for KMS Key (Key Encryption Key) metadata.
|
||||
* Tracks ownership, purpose, and lifecycle of KEKs used in envelope encryption.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "kms_keys")
|
||||
public class KMSKeyVO implements KMSKey {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "uuid", nullable = false)
|
||||
private String uuid;
|
||||
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "description", length = 1024)
|
||||
private String description;
|
||||
|
||||
@Column(name = "kek_label", nullable = false)
|
||||
private String kekLabel;
|
||||
|
||||
@Column(name = "purpose", nullable = false, length = 32)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private KeyPurpose purpose;
|
||||
|
||||
@Column(name = "account_id", nullable = false)
|
||||
private Long accountId;
|
||||
|
||||
@Column(name = "domain_id", nullable = false)
|
||||
private Long domainId;
|
||||
|
||||
@Column(name = "zone_id", nullable = false)
|
||||
private Long zoneId;
|
||||
|
||||
@Column(name = "algorithm", nullable = false, length = 64)
|
||||
private String algorithm;
|
||||
|
||||
@Column(name = "key_bits", nullable = false)
|
||||
private Integer keyBits;
|
||||
|
||||
@Column(name = "enabled", nullable = false)
|
||||
private boolean enabled;
|
||||
|
||||
@Column(name = "hsm_profile_id")
|
||||
private Long hsmProfileId;
|
||||
|
||||
@Column(name = GenericDao.CREATED_COLUMN, nullable = false)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date created;
|
||||
|
||||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date removed;
|
||||
|
||||
public KMSKeyVO(String name, String description, String kekLabel,
|
||||
KeyPurpose purpose, Long accountId, Long domainId,
|
||||
Long zoneId, String algorithm, Integer keyBits
|
||||
) {
|
||||
this();
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.kekLabel = kekLabel;
|
||||
this.purpose = purpose;
|
||||
this.accountId = accountId;
|
||||
this.domainId = domainId;
|
||||
this.zoneId = zoneId;
|
||||
this.algorithm = algorithm;
|
||||
this.keyBits = keyBits;
|
||||
}
|
||||
|
||||
public KMSKeyVO() {
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
this.created = new Date();
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKekLabel() {
|
||||
return kekLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPurpose getPurpose() {
|
||||
return purpose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getKeyBits() {
|
||||
return keyBits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getHsmProfileId() {
|
||||
return hsmProfileId;
|
||||
}
|
||||
|
||||
public void setHsmProfileId(Long hsmProfileId) {
|
||||
this.hsmProfileId = hsmProfileId;
|
||||
}
|
||||
|
||||
public void setRemoved(Date removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setKeyBits(Integer keyBits) {
|
||||
this.keyBits = keyBits;
|
||||
}
|
||||
|
||||
public void setAlgorithm(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public void setZoneId(Long zoneId) {
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
public void setPurpose(KeyPurpose purpose) {
|
||||
this.purpose = purpose;
|
||||
}
|
||||
|
||||
public void setKekLabel(String kekLabel) {
|
||||
this.kekLabel = kekLabel;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setAccountId(Long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
public void setDomainId(Long domainId) {
|
||||
this.domainId = domainId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getEntityType() {
|
||||
return KMSKey.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("KMSKey %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||
this, "id", "uuid", "name", "purpose",
|
||||
"accountId", "zoneId", "enabled"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
// 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.kms;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Database entity for storing wrapped (encrypted) Data Encryption Keys.
|
||||
* Each entry represents a DEK that has been encrypted by a Key Encryption Key (KEK).
|
||||
* KEK metadata is stored in kms_keys table via the kms_key_id foreign key.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "kms_wrapped_key")
|
||||
public class KMSWrappedKeyVO {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "uuid", nullable = false)
|
||||
private String uuid;
|
||||
|
||||
@Column(name = "kms_key_id")
|
||||
private Long kmsKeyId;
|
||||
|
||||
@Column(name = "kek_version_id")
|
||||
private Long kekVersionId;
|
||||
|
||||
@Column(name = "zone_id", nullable = false)
|
||||
private Long zoneId;
|
||||
|
||||
@Column(name = "wrapped_blob", nullable = false)
|
||||
private byte[] wrappedBlob;
|
||||
|
||||
@Column(name = GenericDao.CREATED_COLUMN, nullable = false)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date created;
|
||||
|
||||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date removed;
|
||||
|
||||
public KMSWrappedKeyVO(KMSKeyVO kmsKey, byte[] wrappedBlob) {
|
||||
this();
|
||||
this.kmsKeyId = kmsKey.getId();
|
||||
this.zoneId = kmsKey.getZoneId();
|
||||
this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null;
|
||||
}
|
||||
|
||||
public KMSWrappedKeyVO() {
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
this.created = new Date();
|
||||
}
|
||||
|
||||
public KMSWrappedKeyVO(KMSKeyVO kmsKey, Long kekVersionId, byte[] wrappedBlob) {
|
||||
this();
|
||||
this.kmsKeyId = kmsKey.getId();
|
||||
this.kekVersionId = kekVersionId;
|
||||
this.zoneId = kmsKey.getZoneId();
|
||||
this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null;
|
||||
}
|
||||
|
||||
public KMSWrappedKeyVO(Long kmsKeyId, Long zoneId, byte[] wrappedBlob) {
|
||||
this();
|
||||
this.kmsKeyId = kmsKeyId;
|
||||
this.zoneId = zoneId;
|
||||
this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null;
|
||||
}
|
||||
|
||||
public KMSWrappedKeyVO(Long kmsKeyId, Long kekVersionId, Long zoneId, byte[] wrappedBlob) {
|
||||
this();
|
||||
this.kmsKeyId = kmsKeyId;
|
||||
this.kekVersionId = kekVersionId;
|
||||
this.zoneId = zoneId;
|
||||
this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public Long getKmsKeyId() {
|
||||
return kmsKeyId;
|
||||
}
|
||||
|
||||
public void setKmsKeyId(Long kmsKeyId) {
|
||||
this.kmsKeyId = kmsKeyId;
|
||||
}
|
||||
|
||||
public Long getKekVersionId() {
|
||||
return kekVersionId;
|
||||
}
|
||||
|
||||
public void setKekVersionId(Long kekVersionId) {
|
||||
this.kekVersionId = kekVersionId;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public void setZoneId(Long zoneId) {
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
public byte[] getWrappedBlob() {
|
||||
return wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null;
|
||||
}
|
||||
|
||||
public void setWrappedBlob(byte[] wrappedBlob) {
|
||||
this.wrappedBlob = wrappedBlob != null ? Arrays.copyOf(wrappedBlob, wrappedBlob.length) : null;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void setRemoved(Date removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("KMSWrappedKey %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||
this, "id", "uuid", "kmsKeyId", "kekVersionId", "zoneId", "created", "removed"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// 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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.kms.HSMProfileVO;
|
||||
|
||||
public interface HSMProfileDao extends GenericDao<HSMProfileVO, Long> {
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// 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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import org.apache.cloudstack.kms.HSMProfileVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class HSMProfileDaoImpl extends GenericDaoBase<HSMProfileVO, Long> implements HSMProfileDao {
|
||||
public HSMProfileDaoImpl() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.kms.HSMProfileDetailsVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface HSMProfileDetailsDao extends GenericDao<HSMProfileDetailsVO, Long> {
|
||||
List<HSMProfileDetailsVO> listByProfileId(long profileId);
|
||||
|
||||
void persist(long profileId, String name, String value);
|
||||
|
||||
HSMProfileDetailsVO findDetail(long profileId, String name);
|
||||
|
||||
void deleteDetails(long profileId);
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// 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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.utils.db.SearchCriteria.Op;
|
||||
import org.apache.cloudstack.kms.HSMProfileDetailsVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class HSMProfileDetailsDaoImpl extends GenericDaoBase<HSMProfileDetailsVO, Long> implements HSMProfileDetailsDao {
|
||||
|
||||
protected SearchBuilder<HSMProfileDetailsVO> ProfileSearch;
|
||||
protected SearchBuilder<HSMProfileDetailsVO> DetailSearch;
|
||||
|
||||
public HSMProfileDetailsDaoImpl() {
|
||||
super();
|
||||
|
||||
ProfileSearch = createSearchBuilder();
|
||||
ProfileSearch.and("profileId", ProfileSearch.entity().getResourceId(), Op.EQ);
|
||||
ProfileSearch.done();
|
||||
|
||||
DetailSearch = createSearchBuilder();
|
||||
DetailSearch.and("profileId", DetailSearch.entity().getResourceId(), Op.EQ);
|
||||
DetailSearch.and("name", DetailSearch.entity().getName(), Op.EQ);
|
||||
DetailSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HSMProfileDetailsVO> listByProfileId(long profileId) {
|
||||
SearchCriteria<HSMProfileDetailsVO> sc = ProfileSearch.create();
|
||||
sc.setParameters("profileId", profileId);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persist(long profileId, String name, String value) {
|
||||
HSMProfileDetailsVO vo = new HSMProfileDetailsVO(profileId, name, value);
|
||||
persist(vo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HSMProfileDetailsVO findDetail(long profileId, String name) {
|
||||
SearchCriteria<HSMProfileDetailsVO> sc = DetailSearch.create();
|
||||
sc.setParameters("profileId", profileId);
|
||||
sc.setParameters("name", name);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDetails(long profileId) {
|
||||
SearchCriteria<HSMProfileDetailsVO> sc = ProfileSearch.create();
|
||||
sc.setParameters("profileId", profileId);
|
||||
remove(sc);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// 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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.kms.KMSKekVersionVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface KMSKekVersionDao extends GenericDao<KMSKekVersionVO, Long> {
|
||||
|
||||
KMSKekVersionVO getActiveVersion(Long kmsKeyId);
|
||||
|
||||
/**
|
||||
* Returns Active and Previous versions (usable for decryption)
|
||||
*/
|
||||
List<KMSKekVersionVO> getVersionsForDecryption(Long kmsKeyId);
|
||||
|
||||
List<KMSKekVersionVO> listByKmsKeyId(Long kmsKeyId);
|
||||
|
||||
KMSKekVersionVO findByKmsKeyIdAndVersion(Long kmsKeyId, Integer versionNumber);
|
||||
|
||||
KMSKekVersionVO findByKekLabel(String kekLabel);
|
||||
|
||||
List<KMSKekVersionVO> findByStatus(KMSKekVersionVO.Status status);
|
||||
|
||||
List<KMSKekVersionVO> listByHsmProfileId(Long hsmProfileId);
|
||||
}
|
||||
|
|
@ -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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.kms.KMSKekVersionVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class KMSKekVersionDaoImpl extends GenericDaoBase<KMSKekVersionVO, Long> implements KMSKekVersionDao {
|
||||
|
||||
private final SearchBuilder<KMSKekVersionVO> allFieldSearch;
|
||||
|
||||
public KMSKekVersionDaoImpl() {
|
||||
allFieldSearch = createSearchBuilder();
|
||||
allFieldSearch.and("kmsKeyId", allFieldSearch.entity().getKmsKeyId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("status", allFieldSearch.entity().getStatus(), SearchCriteria.Op.IN);
|
||||
allFieldSearch.and("versionNumber", allFieldSearch.entity().getVersionNumber(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("kekLabel", allFieldSearch.entity().getKekLabel(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("hsmProfileId", allFieldSearch.entity().getHsmProfileId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMSKekVersionVO getActiveVersion(Long kmsKeyId) {
|
||||
SearchCriteria<KMSKekVersionVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("kmsKeyId", kmsKeyId);
|
||||
sc.setParameters("status", KMSKekVersionVO.Status.Active);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSKekVersionVO> getVersionsForDecryption(Long kmsKeyId) {
|
||||
SearchCriteria<KMSKekVersionVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("kmsKeyId", kmsKeyId);
|
||||
sc.setParameters("status", KMSKekVersionVO.Status.Active, KMSKekVersionVO.Status.Previous);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSKekVersionVO> listByKmsKeyId(Long kmsKeyId) {
|
||||
SearchCriteria<KMSKekVersionVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("kmsKeyId", kmsKeyId);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMSKekVersionVO findByKmsKeyIdAndVersion(Long kmsKeyId, Integer versionNumber) {
|
||||
SearchCriteria<KMSKekVersionVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("kmsKeyId", kmsKeyId);
|
||||
sc.setParameters("versionNumber", versionNumber);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMSKekVersionVO findByKekLabel(String kekLabel) {
|
||||
SearchCriteria<KMSKekVersionVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("kekLabel", kekLabel);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSKekVersionVO> findByStatus(KMSKekVersionVO.Status status) {
|
||||
SearchCriteria<KMSKekVersionVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("status", status);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSKekVersionVO> listByHsmProfileId(Long hsmProfileId) {
|
||||
if (hsmProfileId == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
SearchCriteria<KMSKekVersionVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("hsmProfileId", hsmProfileId);
|
||||
return listBy(sc);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// 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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.framework.kms.KeyPurpose;
|
||||
import org.apache.cloudstack.kms.KMSKeyVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface KMSKeyDao extends GenericDao<KMSKeyVO, Long> {
|
||||
|
||||
List<KMSKeyVO> listByAccount(Long accountId, KeyPurpose purpose, Boolean enabled);
|
||||
|
||||
List<KMSKeyVO> listByZone(Long zoneId, KeyPurpose purpose, Boolean enabled);
|
||||
|
||||
long countByHsmProfileId(Long hsmProfileId);
|
||||
|
||||
KMSKeyVO findByNameAndAccountId(String name, long accountId);
|
||||
}
|
||||
|
|
@ -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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.framework.kms.KeyPurpose;
|
||||
import org.apache.cloudstack.kms.KMSKeyVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class KMSKeyDaoImpl extends GenericDaoBase<KMSKeyVO, Long> implements KMSKeyDao {
|
||||
|
||||
private final SearchBuilder<KMSKeyVO> allFieldSearch;
|
||||
|
||||
public KMSKeyDaoImpl() {
|
||||
allFieldSearch = createSearchBuilder();
|
||||
allFieldSearch.and("name", allFieldSearch.entity().getName(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("kekLabel", allFieldSearch.entity().getKekLabel(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("domainId", allFieldSearch.entity().getDomainId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("accountId", allFieldSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("purpose", allFieldSearch.entity().getPurpose(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("enabled", allFieldSearch.entity().isEnabled(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("zoneId", allFieldSearch.entity().getZoneId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("hsmProfileId", allFieldSearch.entity().getHsmProfileId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSKeyVO> listByAccount(Long accountId, KeyPurpose purpose, Boolean enabled) {
|
||||
SearchCriteria<KMSKeyVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("accountId", accountId);
|
||||
sc.setParametersIfNotNull("purpose", purpose);
|
||||
sc.setParametersIfNotNull("enabled", enabled);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSKeyVO> listByZone(Long zoneId, KeyPurpose purpose, Boolean enabled) {
|
||||
SearchCriteria<KMSKeyVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("zoneId", zoneId);
|
||||
sc.setParametersIfNotNull("purpose", purpose);
|
||||
sc.setParametersIfNotNull("enabled", enabled);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByHsmProfileId(Long hsmProfileId) {
|
||||
if (hsmProfileId == null) {
|
||||
return 0;
|
||||
}
|
||||
SearchCriteria<KMSKeyVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("hsmProfileId", hsmProfileId);
|
||||
Integer count = getCount(sc);
|
||||
return count != null ? count : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMSKeyVO findByNameAndAccountId(String name, long accountId) {
|
||||
SearchCriteria<KMSKeyVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("name", name);
|
||||
sc.setParameters("accountId", accountId);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// 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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.kms.KMSWrappedKeyVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface KMSWrappedKeyDao extends GenericDao<KMSWrappedKeyVO, Long> {
|
||||
|
||||
long countByKmsKeyId(Long kmsKeyId);
|
||||
|
||||
/**
|
||||
* Limited variant for batch processing during key rotation
|
||||
*/
|
||||
List<KMSWrappedKeyVO> listByKekVersionId(Long kekVersionId, int limit);
|
||||
|
||||
long countByKekVersionId(Long kekVersionId);
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// 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.kms.dao;
|
||||
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.kms.KMSWrappedKeyVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class KMSWrappedKeyDaoImpl extends GenericDaoBase<KMSWrappedKeyVO, Long> implements KMSWrappedKeyDao {
|
||||
|
||||
private final SearchBuilder<KMSWrappedKeyVO> allFieldSearch;
|
||||
|
||||
public KMSWrappedKeyDaoImpl() {
|
||||
super();
|
||||
|
||||
allFieldSearch = createSearchBuilder();
|
||||
allFieldSearch.and("kmsKeyId", allFieldSearch.entity().getKmsKeyId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("kekVersionId", allFieldSearch.entity().getKekVersionId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("zoneId", allFieldSearch.entity().getZoneId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByKmsKeyId(Long kmsKeyId) {
|
||||
SearchCriteria<KMSWrappedKeyVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("kmsKeyId", kmsKeyId);
|
||||
Integer count = getCount(sc);
|
||||
return count != null ? count.longValue() : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSWrappedKeyVO> listByKekVersionId(Long kekVersionId, int limit) {
|
||||
SearchCriteria<KMSWrappedKeyVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("kekVersionId", kekVersionId);
|
||||
Filter filter = new Filter(limit);
|
||||
return listBy(sc, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByKekVersionId(Long kekVersionId) {
|
||||
if (kekVersionId == null) {
|
||||
return 0;
|
||||
}
|
||||
SearchCriteria<KMSWrappedKeyVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("kekVersionId", kekVersionId);
|
||||
Integer count = getCount(sc);
|
||||
return count != null ? count.longValue() : 0L;
|
||||
}
|
||||
}
|
||||
|
|
@ -310,4 +310,9 @@
|
|||
<bean id="importVMTaskDaoImpl" class="com.cloud.vm.dao.ImportVMTaskDaoImpl" />
|
||||
<bean id="apiKeyPairDaoImpl" class="org.apache.cloudstack.acl.dao.ApiKeyPairDaoImpl" />
|
||||
<bean id="apiKeyPairPermissionsDaoImpl" class="org.apache.cloudstack.acl.dao.ApiKeyPairPermissionsDaoImpl" />
|
||||
<bean id="kmsKeyDaoImpl" class="org.apache.cloudstack.kms.dao.KMSKeyDaoImpl" />
|
||||
<bean id="kmsKekVersionDaoImpl" class="org.apache.cloudstack.kms.dao.KMSKekVersionDaoImpl" />
|
||||
<bean id="kmsWrappedKeyDaoImpl" class="org.apache.cloudstack.kms.dao.KMSWrappedKeyDaoImpl" />
|
||||
<bean id="hsmProfileDaoImpl" class="org.apache.cloudstack.kms.dao.HSMProfileDaoImpl" />
|
||||
<bean id="hsmProfileDetailsDaoImpl" class="org.apache.cloudstack.kms.dao.HSMProfileDetailsDaoImpl" />
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -115,6 +115,171 @@ CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Resource Admin', 'deleteUserKey
|
|||
-- Add conserve mode for VPC offerings
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','conserve_mode', 'tinyint(1) unsigned NULL DEFAULT 0 COMMENT ''True if the VPC offering is IP conserve mode enabled, allowing public IP services to be used across multiple VPC tiers'' ');
|
||||
|
||||
-- KMS HSM Profiles (Generic table for PKCS#11, KMIP, etc.)
|
||||
-- Scoped to account (user-provided) or global/zone (admin-provided)
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`kms_hsm_profiles` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`uuid` VARCHAR(40) NOT NULL,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`protocol` VARCHAR(32) NOT NULL COMMENT 'Protocol for the HSM Profile',
|
||||
|
||||
-- Scoping
|
||||
`account_id` BIGINT UNSIGNED COMMENT 'null = admin-provided (available to all accounts)',
|
||||
`domain_id` BIGINT UNSIGNED COMMENT 'null = zone/global scope',
|
||||
`zone_id` BIGINT UNSIGNED COMMENT 'null = global scope',
|
||||
|
||||
-- Metadata
|
||||
`vendor_name` VARCHAR(64) COMMENT 'HSM vendor',
|
||||
`enabled` BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
`is_public` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'Public profile. Available to all accounts',
|
||||
`created` DATETIME NOT NULL,
|
||||
`removed` DATETIME,
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_uuid` (`uuid`),
|
||||
UNIQUE KEY `uk_account_name` (`account_id`, `name`, `removed`),
|
||||
INDEX `idx_protocol_enabled` (`protocol`, `enabled`, `removed`),
|
||||
INDEX `idx_scoping` (`account_id`, `domain_id`, `zone_id`, `removed`),
|
||||
CONSTRAINT `fk_kms_hsm_profiles__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_kms_hsm_profiles__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_kms_hsm_profiles__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='HSM profiles for KMS providers';
|
||||
|
||||
-- KMS HSM Profile Details (Protocol-specific configuration)
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`kms_hsm_profile_details` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`profile_id` BIGINT UNSIGNED NOT NULL COMMENT 'HSM profile ID',
|
||||
`name` VARCHAR(255) NOT NULL COMMENT 'Config key (e.g. library_path, endpoint, pin, cert_content)',
|
||||
`value` TEXT NOT NULL COMMENT 'Config value (encrypted if sensitive)',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_profile_name` (`profile_id`, `name`),
|
||||
CONSTRAINT `fk_kms_hsm_profile_details__profile_id` FOREIGN KEY (`profile_id`) REFERENCES `kms_hsm_profiles`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Details for HSM profiles (key-value configuration)';
|
||||
|
||||
-- KMS Keys (Key Encryption Key Metadata)
|
||||
-- Account-scoped KEKs for envelope encryption
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`kms_keys` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`uuid` VARCHAR(40) NOT NULL COMMENT 'UUID - user-facing identifier',
|
||||
`name` VARCHAR(255) NOT NULL COMMENT 'User-friendly name',
|
||||
`description` VARCHAR(1024) COMMENT 'User description',
|
||||
`kek_label` VARCHAR(255) NOT NULL COMMENT 'Provider-specific KEK label/ID',
|
||||
`purpose` VARCHAR(32) NOT NULL COMMENT 'Key purpose (VOLUME_ENCRYPTION, TLS_CERT)',
|
||||
`account_id` BIGINT UNSIGNED NOT NULL COMMENT 'Owning account',
|
||||
`domain_id` BIGINT UNSIGNED NOT NULL COMMENT 'Owning domain',
|
||||
`zone_id` BIGINT UNSIGNED NOT NULL COMMENT 'Zone where key is valid',
|
||||
`algorithm` VARCHAR(64) NOT NULL DEFAULT 'AES/GCM/NoPadding' COMMENT 'Encryption algorithm',
|
||||
`key_bits` INT NOT NULL DEFAULT 256 COMMENT 'Key size in bits',
|
||||
`enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Whether the key is enabled for new cryptographic operations',
|
||||
`hsm_profile_id` BIGINT UNSIGNED NOT NULL COMMENT 'Current HSM profile ID for this key',
|
||||
`created` DATETIME NOT NULL COMMENT 'Creation timestamp',
|
||||
`removed` DATETIME COMMENT 'Removal timestamp for soft delete',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_uuid` (`uuid`),
|
||||
INDEX `idx_account_purpose` (`account_id`, `purpose`, `enabled`),
|
||||
INDEX `idx_domain_purpose` (`domain_id`, `purpose`, `enabled`),
|
||||
INDEX `idx_zone_enabled` (`zone_id`, `enabled`),
|
||||
CONSTRAINT `fk_kms_keys__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_kms_keys__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_kms_keys__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_kms_keys__hsm_profile_id` FOREIGN KEY (`hsm_profile_id`) REFERENCES `kms_hsm_profiles`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='KMS Key (KEK) metadata - account-scoped keys for envelope encryption';
|
||||
|
||||
-- KMS KEK Versions (multiple KEKs per KMS key for gradual rotation)
|
||||
-- Supports multiple KEK versions per logical KMS key during rotation
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`kms_kek_versions` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`uuid` VARCHAR(40) NOT NULL COMMENT 'UUID',
|
||||
`kms_key_id` BIGINT UNSIGNED NOT NULL COMMENT 'Reference to kms_keys table',
|
||||
`version_number` INT NOT NULL COMMENT 'Version number (1, 2, 3, ...)',
|
||||
`kek_label` VARCHAR(255) NOT NULL COMMENT 'Provider-specific KEK label/ID for this version',
|
||||
`status` VARCHAR(32) NOT NULL DEFAULT 'Active' COMMENT 'Active, Previous, Archived',
|
||||
`hsm_profile_id` BIGINT UNSIGNED COMMENT 'HSM profile where this KEK version is stored',
|
||||
`created` DATETIME NOT NULL COMMENT 'Creation timestamp',
|
||||
`removed` DATETIME COMMENT 'Removal timestamp for soft delete',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_uuid` (`uuid`),
|
||||
UNIQUE KEY `uk_kms_key_version` (`kms_key_id`, `version_number`, `removed`),
|
||||
INDEX `idx_kms_key_status` (`kms_key_id`, `status`, `removed`),
|
||||
INDEX `idx_kek_label` (`kek_label`),
|
||||
CONSTRAINT `fk_kms_kek_versions__kms_key_id` FOREIGN KEY (`kms_key_id`) REFERENCES `kms_keys`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_kms_kek_versions__hsm_profile_id` FOREIGN KEY (`hsm_profile_id`) REFERENCES `kms_hsm_profiles`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='KEK versions for a KMS key - supports gradual rotation';
|
||||
|
||||
-- KMS Wrapped Keys (Data Encryption Keys)
|
||||
-- Generic table for wrapped DEKs - references kms_keys for metadata and kek_versions for specific KEK version
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`kms_wrapped_key` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`uuid` VARCHAR(40) NOT NULL COMMENT 'UUID',
|
||||
`kms_key_id` BIGINT UNSIGNED COMMENT 'Reference to kms_keys table',
|
||||
`kek_version_id` BIGINT UNSIGNED COMMENT 'Reference to kms_kek_versions table',
|
||||
`zone_id` BIGINT UNSIGNED NOT NULL COMMENT 'Zone ID for zone-scoped keys',
|
||||
`wrapped_blob` VARBINARY(4096) NOT NULL COMMENT 'Encrypted DEK material',
|
||||
`created` DATETIME NOT NULL COMMENT 'Creation timestamp',
|
||||
`removed` DATETIME COMMENT 'Removal timestamp for soft delete',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_uuid` (`uuid`),
|
||||
INDEX `idx_kms_key_id` (`kms_key_id`, `removed`),
|
||||
INDEX `idx_kek_version_id` (`kek_version_id`, `removed`),
|
||||
INDEX `idx_zone_id` (`zone_id`, `removed`),
|
||||
CONSTRAINT `fk_kms_wrapped_key__kms_key_id` FOREIGN KEY (`kms_key_id`) REFERENCES `kms_keys`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_kms_wrapped_key__kek_version_id` FOREIGN KEY (`kek_version_id`) REFERENCES `kms_kek_versions`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_kms_wrapped_key__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='KMS wrapped encryption keys (DEKs) - references kms_keys for KEK metadata and kek_versions for specific version';
|
||||
|
||||
-- Add KMS key reference to volumes table (which KMS key was used)
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'kms_key_id', 'BIGINT UNSIGNED COMMENT ''KMS key ID used for volume encryption''');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.volumes', 'fk_volumes__kms_key_id', '(kms_key_id)', '`kms_keys`(`id`)');
|
||||
|
||||
-- Add KMS wrapped key reference to volumes table
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'kms_wrapped_key_id', 'BIGINT UNSIGNED COMMENT ''KMS wrapped key ID for volume encryption''');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.volumes', 'fk_volumes__kms_wrapped_key_id', '(kms_wrapped_key_id)', '`kms_wrapped_key`(`id`)');
|
||||
|
||||
-- KMS Database Provider KEK Objects (PKCS#11-like object storage)
|
||||
-- Stores KEKs for the database KMS provider in a PKCS#11-compatible format
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`kms_database_kek_objects` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Object handle (PKCS#11 CKA_HANDLE)',
|
||||
`uuid` VARCHAR(40) NOT NULL COMMENT 'UUID',
|
||||
-- PKCS#11 Object Class (CKA_CLASS)
|
||||
`object_class` VARCHAR(32) NOT NULL DEFAULT 'CKO_SECRET_KEY' COMMENT 'PKCS#11 object class (CKO_SECRET_KEY, CKO_PRIVATE_KEY, etc.)',
|
||||
-- PKCS#11 Label (CKA_LABEL) - human-readable identifier
|
||||
`label` VARCHAR(255) NOT NULL COMMENT 'PKCS#11 label (CKA_LABEL) - human-readable identifier',
|
||||
-- PKCS#11 ID (CKA_ID) - application-defined identifier
|
||||
`object_id` VARBINARY(64) COMMENT 'PKCS#11 object ID (CKA_ID) - application-defined identifier',
|
||||
-- Key Type (CKA_KEY_TYPE)
|
||||
`key_type` VARCHAR(32) NOT NULL DEFAULT 'CKK_AES' COMMENT 'PKCS#11 key type (CKK_AES, CKK_RSA, etc.)',
|
||||
-- Key Material (CKA_VALUE) - encrypted KEK material
|
||||
`key_material` VARBINARY(512) NOT NULL COMMENT 'PKCS#11 key value (CKA_VALUE) - encrypted KEK material',
|
||||
-- Key Attributes (PKCS#11 boolean attributes)
|
||||
`is_sensitive` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_SENSITIVE - key material is sensitive',
|
||||
`is_extractable` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'PKCS#11 CKA_EXTRACTABLE - key can be extracted',
|
||||
`is_token` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_TOKEN - object is on token (persistent)',
|
||||
`is_private` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_PRIVATE - object is private',
|
||||
`is_modifiable` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'PKCS#11 CKA_MODIFIABLE - object can be modified',
|
||||
`is_copyable` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'PKCS#11 CKA_COPYABLE - object can be copied',
|
||||
`is_destroyable` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_DESTROYABLE - object can be destroyed',
|
||||
`always_sensitive` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_ALWAYS_SENSITIVE - key was always sensitive',
|
||||
`never_extractable` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'PKCS#11 CKA_NEVER_EXTRACTABLE - key was never extractable',
|
||||
-- Key Metadata
|
||||
`purpose` VARCHAR(32) NOT NULL COMMENT 'Key purpose (VOLUME_ENCRYPTION, TLS_CERT)',
|
||||
`key_bits` INT NOT NULL COMMENT 'Key size in bits (128, 192, 256)',
|
||||
`algorithm` VARCHAR(64) NOT NULL DEFAULT 'AES/GCM/NoPadding' COMMENT 'Encryption algorithm',
|
||||
-- Validity Dates (PKCS#11 CKA_START_DATE, CKA_END_DATE)
|
||||
`start_date` DATETIME COMMENT 'PKCS#11 CKA_START_DATE - key validity start',
|
||||
`end_date` DATETIME COMMENT 'PKCS#11 CKA_END_DATE - key validity end',
|
||||
-- Lifecycle
|
||||
`created` DATETIME NOT NULL COMMENT 'Creation timestamp',
|
||||
`last_used` DATETIME COMMENT 'Last usage timestamp',
|
||||
`removed` DATETIME COMMENT 'Removal timestamp for soft delete',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_uuid` (`uuid`),
|
||||
UNIQUE KEY `uk_label_removed` (`label`, `removed`),
|
||||
INDEX `idx_purpose_removed` (`purpose`, `removed`),
|
||||
INDEX `idx_key_type` (`key_type`, `removed`),
|
||||
INDEX `idx_object_class` (`object_class`, `removed`),
|
||||
INDEX `idx_created` (`created`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='KMS Database Provider KEK Objects - PKCS#11-like object storage for KEKs';
|
||||
|
||||
--- Disable/enable NICs
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics','enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Indicates whether the NIC is enabled or not'' ');
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ SELECT
|
|||
`volumes`.`chain_info` AS `chain_info`,
|
||||
`volumes`.`external_uuid` AS `external_uuid`,
|
||||
`volumes`.`encrypt_format` AS `encrypt_format`,
|
||||
`volumes`.`kms_key_id` AS `kms_key_id`,
|
||||
`kms_keys`.`uuid` AS `kms_key_uuid`,
|
||||
`kms_keys`.`name` AS `kms_key_name`,
|
||||
`volumes`.`kms_wrapped_key_id` AS `kms_wrapped_key_id`,
|
||||
`volumes`.`delete_protection` AS `delete_protection`,
|
||||
`account`.`id` AS `account_id`,
|
||||
`account`.`uuid` AS `account_uuid`,
|
||||
|
|
@ -116,7 +120,7 @@ SELECT
|
|||
`resource_tag_domain`.`uuid` AS `tag_domain_uuid`,
|
||||
`resource_tag_domain`.`name` AS `tag_domain_name`
|
||||
FROM
|
||||
((((((((((((((((((`volumes`
|
||||
(((((((((((((((((((`volumes`
|
||||
JOIN `account`ON
|
||||
((`volumes`.`account_id` = `account`.`id`)))
|
||||
JOIN `domain`ON
|
||||
|
|
@ -129,8 +133,10 @@ LEFT JOIN `vm_instance`ON
|
|||
((`volumes`.`instance_id` = `vm_instance`.`id`)))
|
||||
LEFT JOIN `user_vm`ON
|
||||
((`user_vm`.`id` = `vm_instance`.`id`)))
|
||||
LEFT JOIN `volume_store_ref`ON
|
||||
LEFT JOIN `volume_store_ref` ON
|
||||
((`volumes`.`id` = `volume_store_ref`.`volume_id`)))
|
||||
LEFT JOIN `kms_keys` ON
|
||||
((`volumes`.`kms_key_id` = `kms_keys`.`id`)))
|
||||
LEFT JOIN `service_offering`ON
|
||||
((`vm_instance`.`service_offering_id` = `service_offering`.`id`)))
|
||||
LEFT JOIN `disk_offering`ON
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import com.cloud.utils.db.Transaction;
|
|||
import com.cloud.utils.db.TransactionCallbackNoReturn;
|
||||
import com.cloud.utils.db.TransactionStatus;
|
||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.secret.dao.PassphraseDao;
|
||||
import org.apache.cloudstack.secret.PassphraseVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDetailsDao;
|
||||
|
|
@ -46,6 +47,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
|
|||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
import org.apache.cloudstack.kms.dao.KMSWrappedKeyDao;
|
||||
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
|
||||
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
|
||||
import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager;
|
||||
|
|
@ -98,6 +101,10 @@ public class VolumeObject implements VolumeInfo {
|
|||
@Inject
|
||||
VolumeDataStoreDao volumeStoreDao;
|
||||
@Inject
|
||||
KMSManager kmsManager;
|
||||
@Inject
|
||||
KMSWrappedKeyDao kmsWrappedKeyDao;
|
||||
@Inject
|
||||
ObjectInDataStoreManager objectInStoreMgr;
|
||||
@Inject
|
||||
ResourceLimitService resourceLimitMgr;
|
||||
|
|
@ -900,6 +907,26 @@ public class VolumeObject implements VolumeInfo {
|
|||
volumeVO.setPassphraseId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getKmsKeyId() {
|
||||
return volumeVO.getKmsKeyId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKmsKeyId(Long id) {
|
||||
volumeVO.setKmsKeyId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getKmsWrappedKeyId() {
|
||||
return volumeVO.getKmsWrappedKeyId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKmsWrappedKeyId(Long id) {
|
||||
volumeVO.setKmsWrappedKeyId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes passphrase reference from underlying volume. Also removes the associated passphrase entry if it is the last user.
|
||||
*/
|
||||
|
|
@ -929,9 +956,29 @@ public class VolumeObject implements VolumeInfo {
|
|||
|
||||
/**
|
||||
* Looks up passphrase from underlying volume.
|
||||
* @return passphrase as bytes
|
||||
* Supports both legacy passphrase-based encryption and KMS-based encryption.
|
||||
* @return passphrase/DEK as base64-encoded bytes (UTF-8 bytes of base64 string)
|
||||
*/
|
||||
public byte[] getPassphrase() {
|
||||
// First check for KMS-encrypted volume
|
||||
if (volumeVO.getKmsWrappedKeyId() != null) {
|
||||
try {
|
||||
// Unwrap the DEK from KMS (returns raw binary bytes)
|
||||
byte[] dekBytes = kmsManager.unwrapKey(volumeVO.getKmsWrappedKeyId());
|
||||
// Base64-encode the DEK for consistency with legacy passphrases
|
||||
// and for use with qemu-img which expects base64 format
|
||||
String base64Dek = java.util.Base64.getEncoder().encodeToString(dekBytes);
|
||||
// Zeroize the raw DEK bytes
|
||||
java.util.Arrays.fill(dekBytes, (byte) 0);
|
||||
// Return UTF-8 bytes of the base64 string
|
||||
return base64Dek.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
} catch (KMSException e) {
|
||||
logger.error("Failed to unwrap KMS key for volume {}: {}", volumeVO, e.getMessage(), e);
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to legacy passphrase-based encryption
|
||||
PassphraseVO passphrase = passphraseDao.findById(volumeVO.getPassphraseId());
|
||||
if (passphrase != null) {
|
||||
return passphrase.getPassphrase();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cloud-framework-kms</artifactId>
|
||||
<name>Apache CloudStack Framework - Key Management Service</name>
|
||||
<description>Core KMS framework with provider-agnostic interfaces</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloudstack-framework</artifactId>
|
||||
<version>4.23.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-config</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
// 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.framework.kms;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
/**
|
||||
* Exception class for KMS-related errors with structured error types
|
||||
* to enable proper retry logic and error handling.
|
||||
*/
|
||||
public class KMSException extends CloudRuntimeException {
|
||||
|
||||
/**
|
||||
* Error types for KMS operations to enable intelligent retry logic
|
||||
*/
|
||||
public enum ErrorType {
|
||||
CONNECTION_FAILED(true),
|
||||
/**
|
||||
* Authentication failed (e.g., incorrect PIN)
|
||||
*/
|
||||
AUTHENTICATION_FAILED(false),
|
||||
/**
|
||||
* Provider not initialized or unavailable
|
||||
*/
|
||||
PROVIDER_NOT_INITIALIZED(false),
|
||||
|
||||
/**
|
||||
* KEK not found in backend
|
||||
*/
|
||||
KEK_NOT_FOUND(false),
|
||||
|
||||
/**
|
||||
* KEK with given label already exists
|
||||
*/
|
||||
KEY_ALREADY_EXISTS(false),
|
||||
|
||||
/**
|
||||
* Invalid parameters provided
|
||||
*/
|
||||
INVALID_PARAMETER(false),
|
||||
|
||||
/**
|
||||
* Wrap/unwrap operation failed
|
||||
*/
|
||||
WRAP_UNWRAP_FAILED(true),
|
||||
|
||||
/**
|
||||
* KEK operation (create/delete) failed
|
||||
*/
|
||||
KEK_OPERATION_FAILED(true),
|
||||
|
||||
/**
|
||||
* Health check failed
|
||||
*/
|
||||
HEALTH_CHECK_FAILED(true),
|
||||
|
||||
/**
|
||||
* Transient network or communication error
|
||||
*/
|
||||
TRANSIENT_ERROR(true),
|
||||
|
||||
/**
|
||||
* Unknown error
|
||||
*/
|
||||
UNKNOWN(false);
|
||||
|
||||
private final boolean retryable;
|
||||
|
||||
ErrorType(boolean retryable) {
|
||||
this.retryable = retryable;
|
||||
}
|
||||
|
||||
public boolean isRetryable() {
|
||||
return retryable;
|
||||
}
|
||||
}
|
||||
|
||||
private final ErrorType errorType;
|
||||
|
||||
public KMSException(String message) {
|
||||
super(message);
|
||||
this.errorType = ErrorType.UNKNOWN;
|
||||
}
|
||||
|
||||
public KMSException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.errorType = ErrorType.UNKNOWN;
|
||||
}
|
||||
|
||||
public KMSException(ErrorType errorType, String message) {
|
||||
super(message);
|
||||
this.errorType = errorType;
|
||||
}
|
||||
|
||||
public KMSException(ErrorType errorType, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.errorType = errorType;
|
||||
}
|
||||
|
||||
public static KMSException providerNotInitialized(String details) {
|
||||
return new KMSException(ErrorType.PROVIDER_NOT_INITIALIZED,
|
||||
"KMS provider not initialized: " + details);
|
||||
}
|
||||
|
||||
public static KMSException kekNotFound(String kekId) {
|
||||
return new KMSException(ErrorType.KEK_NOT_FOUND,
|
||||
"KEK not found: " + kekId);
|
||||
}
|
||||
|
||||
public static KMSException keyAlreadyExists(String details) {
|
||||
return new KMSException(ErrorType.KEY_ALREADY_EXISTS,
|
||||
"Key already exists: " + details);
|
||||
}
|
||||
|
||||
public static KMSException invalidParameter(String details) {
|
||||
return new KMSException(ErrorType.INVALID_PARAMETER,
|
||||
"Invalid parameter: " + details);
|
||||
}
|
||||
|
||||
public static KMSException wrapUnwrapFailed(String details, Throwable cause) {
|
||||
return new KMSException(ErrorType.WRAP_UNWRAP_FAILED,
|
||||
"Wrap/unwrap operation failed: " + details, cause);
|
||||
}
|
||||
|
||||
public static KMSException wrapUnwrapFailed(String details) {
|
||||
return new KMSException(ErrorType.WRAP_UNWRAP_FAILED,
|
||||
"Wrap/unwrap operation failed: " + details);
|
||||
}
|
||||
|
||||
public static KMSException kekOperationFailed(String details, Throwable cause) {
|
||||
return new KMSException(ErrorType.KEK_OPERATION_FAILED,
|
||||
"KEK operation failed: " + details, cause);
|
||||
}
|
||||
|
||||
public static KMSException kekOperationFailed(String details) {
|
||||
return new KMSException(ErrorType.KEK_OPERATION_FAILED,
|
||||
"KEK operation failed: " + details);
|
||||
}
|
||||
|
||||
public static KMSException healthCheckFailed(String details, Throwable cause) {
|
||||
return new KMSException(ErrorType.HEALTH_CHECK_FAILED,
|
||||
"Health check failed: " + details, cause);
|
||||
}
|
||||
|
||||
public static KMSException transientError(String details, Throwable cause) {
|
||||
return new KMSException(ErrorType.TRANSIENT_ERROR,
|
||||
"Transient error: " + details, cause);
|
||||
}
|
||||
|
||||
public ErrorType getErrorType() {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KMSException{" +
|
||||
"errorType=" + errorType +
|
||||
", retryable=" + isRetryable() +
|
||||
", message='" + getMessage() + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public boolean isRetryable() {
|
||||
return errorType.isRetryable();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
// 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.framework.kms;
|
||||
|
||||
import com.cloud.utils.component.Adapter;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
|
||||
/**
|
||||
* Abstract provider contract for Key Management Service operations.
|
||||
* <p>
|
||||
* Implementations provide the cryptographic backend (HSM via PKCS#11, database, cloud KMS, etc.)
|
||||
* for secure key wrapping/unwrapping using envelope encryption.
|
||||
* <p>
|
||||
* Design principles:
|
||||
* - KEKs (Key Encryption Keys) never leave the secure backend
|
||||
* - DEKs (Data Encryption Keys) are wrapped by KEKs for storage
|
||||
* - Plaintext DEKs exist only transiently in memory during wrap/unwrap
|
||||
* - All operations are purpose-scoped to prevent key reuse
|
||||
* <p>
|
||||
* Thread-safety: Implementations must be thread-safe for concurrent operations.
|
||||
*/
|
||||
public interface KMSProvider extends Configurable, Adapter {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given HSM profile configuration key name refers
|
||||
* to a
|
||||
* sensitive value (PIN, password, secret, or private key) that must be
|
||||
* encrypted at
|
||||
* rest and masked in API responses.
|
||||
*
|
||||
* <p>
|
||||
* This is a shared naming-convention helper used by both KMS providers (when
|
||||
* loading/storing profile details) and the KMS manager (when building API
|
||||
* responses).
|
||||
*
|
||||
* @param key configuration key name (case-insensitive); null returns false
|
||||
* @return true if the key is considered sensitive
|
||||
*/
|
||||
static boolean isSensitiveKey(String key) {
|
||||
if (key == null) {
|
||||
return false;
|
||||
}
|
||||
return key.equalsIgnoreCase("pin") ||
|
||||
key.equalsIgnoreCase("password") ||
|
||||
key.toLowerCase().contains("secret") ||
|
||||
key.equalsIgnoreCase("private_key");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique name of this provider
|
||||
*
|
||||
* @return provider name (e.g., "database", "pkcs11")
|
||||
*/
|
||||
String getProviderName();
|
||||
|
||||
/**
|
||||
* Create a new Key Encryption Key (KEK) in the secure backend.
|
||||
* Delegates to {@link #createKek(KeyPurpose, String, int, Long)} with null profile ID.
|
||||
*
|
||||
* @param purpose the purpose/scope for this KEK
|
||||
* @param label human-readable label for the KEK (must be unique within purpose)
|
||||
* @param keyBits key size in bits (typically 128, 192, or 256)
|
||||
* @return the KEK identifier (label or handle) for later reference
|
||||
* @throws KMSException if KEK creation fails
|
||||
*/
|
||||
default String createKek(KeyPurpose purpose, String label, int keyBits) throws KMSException {
|
||||
return createKek(purpose, label, keyBits, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Key Encryption Key (KEK) in the secure backend with explicit HSM profile.
|
||||
*
|
||||
* @param purpose the purpose/scope for this KEK
|
||||
* @param label human-readable label for the KEK (must be unique within purpose)
|
||||
* @param keyBits key size in bits (typically 128, 192, or 256)
|
||||
* @param hsmProfileId optional HSM profile ID to create the KEK in (null for auto-resolution/default)
|
||||
* @return the KEK identifier (label or handle) for later reference
|
||||
* @throws KMSException if KEK creation fails
|
||||
*/
|
||||
String createKek(KeyPurpose purpose, String label, int keyBits, Long hsmProfileId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Delete a KEK from the secure backend.
|
||||
* WARNING: This will make all DEKs wrapped by this KEK unrecoverable.
|
||||
*
|
||||
* @param kekId the KEK identifier to delete
|
||||
* @throws KMSException if deletion fails or KEK not found
|
||||
*/
|
||||
void deleteKek(String kekId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Validates the configuration details for this provider before saving an HSM
|
||||
* profile.
|
||||
* Implementations should override this to perform provider-specific validation.
|
||||
*
|
||||
* @param details the configuration details to validate
|
||||
* @throws KMSException if validation fails
|
||||
*/
|
||||
default void validateProfileConfig(java.util.Map<String, String> details) throws KMSException {
|
||||
// default no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a KEK exists and is accessible
|
||||
*
|
||||
* @param kekId the KEK identifier to check
|
||||
* @return true if KEK is available
|
||||
* @throws KMSException if check fails
|
||||
*/
|
||||
boolean isKekAvailable(String kekId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Wrap (encrypt) a plaintext Data Encryption Key with a KEK.
|
||||
* Delegates to {@link #wrapKey(byte[], KeyPurpose, String, Long)} with null profile ID.
|
||||
*
|
||||
* @param plainDek the plaintext DEK to wrap (caller must zeroize after call)
|
||||
* @param purpose the intended purpose of this DEK
|
||||
* @param kekLabel the label of the KEK to use for wrapping
|
||||
* @return WrappedKey containing the encrypted DEK and metadata
|
||||
* @throws KMSException if wrapping fails or KEK not found
|
||||
*/
|
||||
default WrappedKey wrapKey(byte[] plainDek, KeyPurpose purpose, String kekLabel) throws KMSException {
|
||||
return wrapKey(plainDek, purpose, kekLabel, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap (encrypt) a plaintext Data Encryption Key with a KEK using explicit HSM profile.
|
||||
*
|
||||
* @param plainDek the plaintext DEK to wrap (caller must zeroize after call)
|
||||
* @param purpose the intended purpose of this DEK
|
||||
* @param kekLabel the label of the KEK to use for wrapping
|
||||
* @param hsmProfileId optional HSM profile ID to use (null for auto-resolution/default)
|
||||
* @return WrappedKey containing the encrypted DEK and metadata
|
||||
* @throws KMSException if wrapping fails or KEK not found
|
||||
*/
|
||||
WrappedKey wrapKey(byte[] plainDek, KeyPurpose purpose, String kekLabel, Long hsmProfileId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Unwrap (decrypt) a wrapped DEK to obtain the plaintext key.
|
||||
* Delegates to {@link #unwrapKey(WrappedKey, Long)} with null profile ID.
|
||||
* <p>
|
||||
* SECURITY: Caller MUST zeroize the returned byte array after use
|
||||
*
|
||||
* @param wrappedKey the wrapped key to decrypt
|
||||
* @return plaintext DEK (caller must zeroize!)
|
||||
* @throws KMSException if unwrapping fails or KEK not found
|
||||
*/
|
||||
default byte[] unwrapKey(WrappedKey wrappedKey) throws KMSException {
|
||||
return unwrapKey(wrappedKey, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap (decrypt) a wrapped DEK to obtain the plaintext key using explicit HSM profile.
|
||||
* <p>
|
||||
* SECURITY: Caller MUST zeroize the returned byte array after use
|
||||
*
|
||||
* @param wrappedKey the wrapped key to decrypt
|
||||
* @param hsmProfileId optional HSM profile ID to use (null for auto-resolution/default)
|
||||
* @return plaintext DEK (caller must zeroize!)
|
||||
* @throws KMSException if unwrapping fails or KEK not found
|
||||
*/
|
||||
byte[] unwrapKey(WrappedKey wrappedKey, Long hsmProfileId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Generate a new random DEK and immediately wrap it with a KEK.
|
||||
* Delegates to {@link #generateAndWrapDek(KeyPurpose, String, int, Long)} with null profile ID.
|
||||
* (convenience method combining generation + wrapping)
|
||||
*
|
||||
* @param purpose the intended purpose of the new DEK
|
||||
* @param kekLabel the label of the KEK to use for wrapping
|
||||
* @param keyBits DEK size in bits (typically 128, 192, or 256)
|
||||
* @return WrappedKey containing the newly generated and wrapped DEK
|
||||
* @throws KMSException if generation or wrapping fails
|
||||
*/
|
||||
default WrappedKey generateAndWrapDek(KeyPurpose purpose, String kekLabel, int keyBits) throws KMSException {
|
||||
return generateAndWrapDek(purpose, kekLabel, keyBits, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new random DEK and immediately wrap it with a KEK using explicit HSM profile.
|
||||
* (convenience method combining generation + wrapping)
|
||||
*
|
||||
* @param purpose the intended purpose of the new DEK
|
||||
* @param kekLabel the label of the KEK to use for wrapping
|
||||
* @param keyBits DEK size in bits (typically 128, 192, or 256)
|
||||
* @param hsmProfileId optional HSM profile ID to use (null for auto-resolution/default)
|
||||
* @return WrappedKey containing the newly generated and wrapped DEK
|
||||
* @throws KMSException if generation or wrapping fails
|
||||
*/
|
||||
WrappedKey generateAndWrapDek(KeyPurpose purpose, String kekLabel, int keyBits,
|
||||
Long hsmProfileId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Rewrap a DEK with a different KEK (used during key rotation).
|
||||
* Delegates to {@link #rewrapKey(WrappedKey, String, Long)} with null profile ID.
|
||||
* This unwraps with the old KEK and wraps with the new KEK without exposing the plaintext DEK.
|
||||
*
|
||||
* @param oldWrappedKey the currently wrapped key
|
||||
* @param newKekLabel the label of the new KEK to wrap with
|
||||
* @return new WrappedKey encrypted with the new KEK
|
||||
* @throws KMSException if rewrapping fails
|
||||
*/
|
||||
default WrappedKey rewrapKey(WrappedKey oldWrappedKey, String newKekLabel) throws KMSException {
|
||||
return rewrapKey(oldWrappedKey, newKekLabel, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrap a DEK with a different KEK (used during key rotation) using explicit target HSM profile.
|
||||
* This unwraps with the old KEK and wraps with the new KEK without exposing the plaintext DEK.
|
||||
*
|
||||
* @param oldWrappedKey the currently wrapped key
|
||||
* @param newKekLabel the label of the new KEK to wrap with
|
||||
* @param targetHsmProfileId optional target HSM profile ID to wrap with (null for auto-resolution/default)
|
||||
* @return new WrappedKey encrypted with the new KEK
|
||||
* @throws KMSException if rewrapping fails
|
||||
*/
|
||||
WrappedKey rewrapKey(WrappedKey oldWrappedKey, String newKekLabel, Long targetHsmProfileId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Perform health check on the provider backend
|
||||
*
|
||||
* @return true if provider is healthy and operational
|
||||
* @throws KMSException if health check fails with critical error
|
||||
*/
|
||||
boolean healthCheck() throws KMSException;
|
||||
|
||||
/**
|
||||
* Invalidates any cached state (config, sessions) associated with the given HSM profile.
|
||||
* Must be called after an HSM profile is updated or deleted so that the next operation
|
||||
* re-reads the profile details from the database instead of using stale cached values.
|
||||
*
|
||||
* <p>Providers that do not cache per-profile state (e.g. the database provider) can
|
||||
* leave this as a no-op.
|
||||
*
|
||||
* @param profileId the HSM profile ID whose cache should be evicted
|
||||
*/
|
||||
default void invalidateProfileCache(Long profileId) {
|
||||
// no-op for providers that don't cache per-profile state
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// 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.framework.kms;
|
||||
|
||||
/**
|
||||
* Defines the purpose/usage scope for cryptographic keys in the KMS system.
|
||||
* This enables proper key segregation and prevents key reuse across different contexts.
|
||||
*/
|
||||
public enum KeyPurpose {
|
||||
/**
|
||||
* Keys used for encrypting VM disk volumes (LUKS, encrypted storage)
|
||||
*/
|
||||
VOLUME_ENCRYPTION("volume", "Volume disk encryption keys"),
|
||||
|
||||
/**
|
||||
* Keys used for protecting TLS certificate private keys
|
||||
*/
|
||||
TLS_CERT("tls", "TLS certificate private keys");
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
|
||||
KeyPurpose(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string name to KeyPurpose enum
|
||||
*
|
||||
* @param name the string representation of the purpose
|
||||
* @return matching KeyPurpose
|
||||
* @throws IllegalArgumentException if no matching purpose found
|
||||
*/
|
||||
public static KeyPurpose fromString(String name) {
|
||||
for (KeyPurpose purpose : KeyPurpose.values()) {
|
||||
if (purpose.getName().equalsIgnoreCase(name)) {
|
||||
return purpose;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown KeyPurpose: " + name);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a globally unique, collision-resistant KEK label with context
|
||||
*
|
||||
* @param domainId the domain ID associated with this key
|
||||
* @param accountId the account ID associated with this key
|
||||
* @param uuid the unique identifier of the key entity
|
||||
* @param version the version number of the key
|
||||
* @return formatted KEK label (e.g., "volume-kek-1-2-a8054d8f-...-1")
|
||||
*/
|
||||
public String generateKekLabel(long domainId, long accountId, String uuid, int version) {
|
||||
return name + "-kek-" + domainId + "-" + accountId + "-" + uuid.replace("-", "") + "-v" + version;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
// 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.framework.kms;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Immutable Data Transfer Object representing an encrypted (wrapped) Data Encryption Key.
|
||||
* The wrapped key material contains the DEK encrypted by a Key Encryption Key (KEK)
|
||||
* stored in a secure backend (HSM, database, etc.).
|
||||
* <p>
|
||||
* This follows the envelope encryption pattern:
|
||||
* - DEK: encrypts actual data (e.g., disk volume)
|
||||
* - KEK: encrypts the DEK (never leaves secure storage)
|
||||
* - Wrapped Key: DEK encrypted by KEK, safe to store in database
|
||||
*/
|
||||
public class WrappedKey {
|
||||
private final String uuid;
|
||||
private final String kekId;
|
||||
private final KeyPurpose purpose;
|
||||
private final String algorithm;
|
||||
private final byte[] wrappedKeyMaterial;
|
||||
private final String providerName;
|
||||
private final Date created;
|
||||
private final Long zoneId;
|
||||
|
||||
/**
|
||||
* Create a new WrappedKey instance
|
||||
*
|
||||
* @param kekId ID/label of the KEK used to wrap this key
|
||||
* @param purpose the intended use of this key
|
||||
* @param algorithm encryption algorithm (e.g., "AES/GCM/NoPadding")
|
||||
* @param wrappedKeyMaterial the encrypted DEK blob
|
||||
* @param providerName name of the KMS provider that created this key
|
||||
* @param created timestamp when key was wrapped
|
||||
* @param zoneId optional zone ID for zone-scoped keys
|
||||
*/
|
||||
public WrappedKey(String kekId, KeyPurpose purpose, String algorithm,
|
||||
byte[] wrappedKeyMaterial, String providerName,
|
||||
Date created, Long zoneId) {
|
||||
this(null, kekId, purpose, algorithm, wrappedKeyMaterial, providerName, created, zoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for database-loaded keys with ID
|
||||
*/
|
||||
public WrappedKey(String uuid, String kekId, KeyPurpose purpose, String algorithm,
|
||||
byte[] wrappedKeyMaterial, String providerName,
|
||||
Date created, Long zoneId) {
|
||||
this.uuid = uuid;
|
||||
this.kekId = Objects.requireNonNull(kekId, "kekId cannot be null");
|
||||
this.purpose = Objects.requireNonNull(purpose, "purpose cannot be null");
|
||||
this.algorithm = Objects.requireNonNull(algorithm, "algorithm cannot be null");
|
||||
this.providerName = providerName;
|
||||
|
||||
if (wrappedKeyMaterial == null || wrappedKeyMaterial.length == 0) {
|
||||
throw new IllegalArgumentException("wrappedKeyMaterial cannot be null or empty");
|
||||
}
|
||||
this.wrappedKeyMaterial = Arrays.copyOf(wrappedKeyMaterial, wrappedKeyMaterial.length);
|
||||
|
||||
this.created = created != null ? new Date(created.getTime()) : new Date();
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getKekId() {
|
||||
return kekId;
|
||||
}
|
||||
|
||||
public KeyPurpose getPurpose() {
|
||||
return purpose;
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get wrapped key material. Returns a defensive copy to prevent modification.
|
||||
* Caller is responsible for zeroizing the returned array after use.
|
||||
*/
|
||||
public byte[] getWrappedKeyMaterial() {
|
||||
return Arrays.copyOf(wrappedKeyMaterial, wrappedKeyMaterial.length);
|
||||
}
|
||||
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created != null ? new Date(created.getTime()) : null;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WrappedKey{" +
|
||||
"uuid='" + uuid + '\'' +
|
||||
", kekId='" + kekId + '\'' +
|
||||
", purpose=" + purpose +
|
||||
", algorithm='" + algorithm + '\'' +
|
||||
", providerName='" + providerName + '\'' +
|
||||
", materialLength=" + (wrappedKeyMaterial != null ? wrappedKeyMaterial.length : 0) +
|
||||
", created=" + created +
|
||||
", zoneId=" + zoneId +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@
|
|||
<module>extensions</module>
|
||||
<module>ipc</module>
|
||||
<module>jobs</module>
|
||||
<module>kms</module>
|
||||
<module>managed-context</module>
|
||||
<module>quota</module>
|
||||
<module>rest</module>
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
hosts = hosts.stream().filter(host -> !constraints.antiAffinityOccupiedHosts.contains(host.getId())).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(hosts)) {
|
||||
String msg = String.format("Cannot find capacity for Kubernetes cluster: host anti-affinity requires each VM on a separate host, " +
|
||||
"but all %d available hosts in zone %s are already occupied by existing cluster VMs",
|
||||
"but all %d available hosts in zone %s are already occupied by existing cluster VMs",
|
||||
constraints.antiAffinityOccupiedHosts.size(), zone.getName());
|
||||
throw new InsufficientServerCapacityException(msg, DataCenter.class, zone.getId());
|
||||
}
|
||||
|
|
@ -198,8 +198,8 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
|
||||
protected DeployDestination plan(final long nodesCount, final DataCenter zone, final ServiceOffering offering,
|
||||
final Long domainId, final Long accountId, final Hypervisor.HypervisorType hypervisorType,
|
||||
CPU.CPUArch arch, KubernetesClusterNodeType nodeType) throws InsufficientServerCapacityException {
|
||||
final Long domainId, final Long accountId, final Hypervisor.HypervisorType hypervisorType,
|
||||
CPU.CPUArch arch, KubernetesClusterNodeType nodeType) throws InsufficientServerCapacityException {
|
||||
final int cpu_requested = offering.getCpu() * offering.getSpeed();
|
||||
final long ram_requested = offering.getRamSize() * 1024L * 1024L;
|
||||
boolean useDedicatedHosts = false;
|
||||
|
|
@ -295,7 +295,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
String msg;
|
||||
if (affinityConstraints.hasHostAntiAffinity) {
|
||||
msg = String.format("Cannot find enough capacity for Kubernetes cluster (requested cpu=%d memory=%s) with offering: %s. " +
|
||||
"Host anti-affinity requires %d separate hosts but not enough suitable hosts are available in zone %s",
|
||||
"Host anti-affinity requires %d separate hosts but not enough suitable hosts are available in zone %s",
|
||||
cpu_requested * nodesCount, toHumanReadableSize(ram_requested * nodesCount), offering.getName(),
|
||||
nodesCount, zone.getName());
|
||||
} else {
|
||||
|
|
@ -399,7 +399,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
|
||||
protected List<UserVm> provisionKubernetesClusterNodeVms(final long nodeCount, final int offset,
|
||||
final String controlIpAddress, final Long domainId, final Long accountId) throws ManagementServerException,
|
||||
final String controlIpAddress, final Long domainId, final Long accountId) throws ManagementServerException,
|
||||
ResourceUnavailableException, InsufficientCapacityException {
|
||||
List<UserVm> nodes = new ArrayList<>();
|
||||
for (int i = offset + 1; i <= nodeCount; i++) {
|
||||
|
|
@ -468,12 +468,14 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
nodeVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, workerNodeTemplate, networkIds, securityGroupIds, owner,
|
||||
hostName, hostName, null, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, null, null, keypairs,
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null,
|
||||
null, true, null, UserVmManager.CKS_NODE, null, null);
|
||||
null, true, null, null, UserVmManager.CKS_NODE, null, null);
|
||||
} else {
|
||||
nodeVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, workerNodeTemplate, networkIds, owner,
|
||||
hostName, hostName, null, null, null, null,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, null, null, keypairs,
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap,
|
||||
null, null, null, null, true,
|
||||
UserVmManager.CKS_NODE, null, null, null, null);
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Created node VM : {}, {} in the Kubernetes cluster : {}", hostName, nodeVm, kubernetesCluster.getName());
|
||||
|
|
@ -504,7 +506,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
|
||||
protected void provisionPublicIpPortForwardingRule(IpAddress publicIp, Network network, Account account,
|
||||
final long vmId, final int sourcePort, final int destPort) throws NetworkRuleConflictException, ResourceUnavailableException {
|
||||
final long vmId, final int sourcePort, final int destPort) throws NetworkRuleConflictException, ResourceUnavailableException {
|
||||
final long publicIpId = publicIp.getId();
|
||||
final long networkId = network.getId();
|
||||
final long accountId = account.getId();
|
||||
|
|
@ -543,7 +545,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
* @throws NetworkRuleConflictException
|
||||
*/
|
||||
protected void provisionSshPortForwardingRules(IpAddress publicIp, Network network, Account account,
|
||||
List<Long> clusterVMIds, Map<Long, Integer> vmIdPortMap) throws ResourceUnavailableException,
|
||||
List<Long> clusterVMIds, Map<Long, Integer> vmIdPortMap) throws ResourceUnavailableException,
|
||||
NetworkRuleConflictException {
|
||||
if (!CollectionUtils.isEmpty(clusterVMIds)) {
|
||||
int defaultNodesCount = clusterVMIds.size() - vmIdPortMap.size();
|
||||
|
|
@ -566,7 +568,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
Integer startPort = firewallRule.getSourcePortStart();
|
||||
Integer endPort = firewallRule.getSourcePortEnd();
|
||||
if (startPort != null && startPort == CLUSTER_API_PORT &&
|
||||
endPort != null && endPort == CLUSTER_API_PORT) {
|
||||
endPort != null && endPort == CLUSTER_API_PORT) {
|
||||
rule = firewallRule;
|
||||
firewallService.revokeIngressFwRule(firewallRule.getId(), true);
|
||||
logger.debug("The API firewall rule [%s] with the id [%s] was revoked",firewallRule.getName(),firewallRule.getId());
|
||||
|
|
@ -613,7 +615,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
|
||||
protected void removePortForwardingRules(final IpAddress publicIp, final Network network, final Account account, int startPort, int endPort)
|
||||
throws ResourceUnavailableException {
|
||||
throws ResourceUnavailableException {
|
||||
List<PortForwardingRuleVO> pfRules = portForwardingRulesDao.listByNetwork(network.getId());
|
||||
for (PortForwardingRuleVO pfRule : pfRules) {
|
||||
if (startPort <= pfRule.getSourcePortStart() && pfRule.getSourcePortStart() <= endPort) {
|
||||
|
|
@ -626,10 +628,10 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
|
||||
protected void removeLoadBalancingRule(final IpAddress publicIp, final Network network,
|
||||
final Account account) throws ResourceUnavailableException {
|
||||
final Account account) throws ResourceUnavailableException {
|
||||
List<LoadBalancerVO> loadBalancerRules = loadBalancerDao.listByIpAddress(publicIp.getId());
|
||||
loadBalancerRules.stream().filter(lbRules -> lbRules.getNetworkId() == network.getId() && lbRules.getAccountId() == account.getId() && lbRules.getSourcePortStart() == CLUSTER_API_PORT
|
||||
&& lbRules.getSourcePortEnd() == CLUSTER_API_PORT).forEach(lbRule -> {
|
||||
&& lbRules.getSourcePortEnd() == CLUSTER_API_PORT).forEach(lbRule -> {
|
||||
lbService.deleteLoadBalancerRule(lbRule.getId(), true);
|
||||
logger.debug("The load balancing rule with the Id: {} was removed",lbRule.getId());
|
||||
});
|
||||
|
|
@ -665,12 +667,12 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
IllegalAccessException, ResourceUnavailableException {
|
||||
List<NetworkACLItemVO> aclItems = networkACLItemDao.listByACL(network.getNetworkACLId());
|
||||
aclItems = aclItems.stream().filter(networkACLItem -> (networkACLItem.getProtocol() != null &&
|
||||
networkACLItem.getProtocol().equals("TCP") &&
|
||||
networkACLItem.getSourcePortStart() != null &&
|
||||
networkACLItem.getSourcePortStart().equals(startPort) &&
|
||||
networkACLItem.getSourcePortEnd() != null &&
|
||||
networkACLItem.getSourcePortEnd().equals(endPort) &&
|
||||
networkACLItem.getAction().equals(NetworkACLItem.Action.Allow)))
|
||||
networkACLItem.getProtocol().equals("TCP") &&
|
||||
networkACLItem.getSourcePortStart() != null &&
|
||||
networkACLItem.getSourcePortStart().equals(startPort) &&
|
||||
networkACLItem.getSourcePortEnd() != null &&
|
||||
networkACLItem.getSourcePortEnd().equals(endPort) &&
|
||||
networkACLItem.getAction().equals(NetworkACLItem.Action.Allow)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (NetworkACLItemVO aclItem : aclItems) {
|
||||
|
|
@ -679,7 +681,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
|
||||
protected void provisionLoadBalancerRule(final IpAddress publicIp, final Network network,
|
||||
final Account account, final List<Long> clusterVMIds, final int port) throws NetworkRuleConflictException,
|
||||
final Account account, final List<Long> clusterVMIds, final int port) throws NetworkRuleConflictException,
|
||||
InsufficientAddressCapacityException {
|
||||
LoadBalancer lb = lbService.createPublicLoadBalancerRule(null, "api-lb", "LB rule for API access",
|
||||
port, port, port, port,
|
||||
|
|
@ -879,11 +881,11 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
}
|
||||
|
||||
protected KubernetesClusterVO updateKubernetesClusterEntry(final Long cores, final Long memory, final Long size,
|
||||
final Long serviceOfferingId, final Boolean autoscaleEnabled,
|
||||
final Long minSize, final Long maxSize,
|
||||
final KubernetesClusterNodeType nodeType,
|
||||
final boolean updateNodeOffering,
|
||||
final boolean updateClusterOffering) {
|
||||
final Long serviceOfferingId, final Boolean autoscaleEnabled,
|
||||
final Long minSize, final Long maxSize,
|
||||
final KubernetesClusterNodeType nodeType,
|
||||
final boolean updateNodeOffering,
|
||||
final boolean updateClusterOffering) {
|
||||
return Transaction.execute((TransactionCallback<KubernetesClusterVO>) status -> {
|
||||
KubernetesClusterVO updatedCluster = kubernetesClusterDao.createForUpdate(kubernetesCluster.getId());
|
||||
|
||||
|
|
@ -941,9 +943,9 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
try {
|
||||
if (enable) {
|
||||
String command = String.format("sudo /opt/bin/autoscale-kube-cluster -i %s -e -M %d -m %d",
|
||||
kubernetesCluster.getUuid(), maxSize, minSize);
|
||||
kubernetesCluster.getUuid(), maxSize, minSize);
|
||||
Pair<Boolean, String> result = SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(),
|
||||
pkFile, null, command, 10000, 10000, 60000);
|
||||
pkFile, null, command, 10000, 10000, 60000);
|
||||
|
||||
// Maybe the file isn't present. Try and copy it
|
||||
if (!result.first()) {
|
||||
|
|
@ -953,12 +955,12 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
|
||||
if (!createCloudStackSecret(keys)) {
|
||||
logTransitStateAndThrow(Level.ERROR, String.format("Failed to setup keys for Kubernetes cluster %s",
|
||||
kubernetesCluster.getName()), kubernetesCluster.getId(), KubernetesCluster.Event.OperationFailed);
|
||||
kubernetesCluster.getName()), kubernetesCluster.getId(), KubernetesCluster.Event.OperationFailed);
|
||||
}
|
||||
|
||||
// If at first you don't succeed ...
|
||||
result = SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(),
|
||||
pkFile, null, command, 10000, 10000, 60000);
|
||||
pkFile, null, command, 10000, 10000, 60000);
|
||||
if (!result.first()) {
|
||||
throw new CloudRuntimeException(result.second());
|
||||
}
|
||||
|
|
@ -966,7 +968,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu
|
|||
updateKubernetesClusterEntry(true, minSize, maxSize);
|
||||
} else {
|
||||
Pair<Boolean, String> result = SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(),
|
||||
pkFile, null, String.format("sudo /opt/bin/autoscale-kube-cluster -d"),
|
||||
pkFile, null, String.format("sudo /opt/bin/autoscale-kube-cluster -d"),
|
||||
10000, 10000, 60000);
|
||||
if (!result.first()) {
|
||||
throw new CloudRuntimeException(result.second());
|
||||
|
|
|
|||
|
|
@ -142,8 +142,8 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
}
|
||||
|
||||
private Pair<String, String> getKubernetesControlNodeConfig(final String controlNodeIp, final String serverIp,
|
||||
final List<Network.IpAddresses> etcdIps, final String hostName, final boolean haSupported,
|
||||
final boolean ejectIso, final boolean externalCni, final boolean setupCsi) throws IOException {
|
||||
final List<Network.IpAddresses> etcdIps, final String hostName, final boolean haSupported,
|
||||
final boolean ejectIso, final boolean externalCni, final boolean setupCsi) throws IOException {
|
||||
String k8sControlNodeConfig = readK8sConfigFile("/conf/k8s-control-node.yml");
|
||||
final String apiServerCert = "{{ k8s_control_node.apiserver.crt }}";
|
||||
final String apiServerKey = "{{ k8s_control_node.apiserver.key }}";
|
||||
|
|
@ -273,19 +273,21 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
List<Long> affinityGroupIds = getMergedAffinityGroupIds(CONTROL, domainId, accountId);
|
||||
String userDataDetails = kubernetesCluster.getCniConfigDetails();
|
||||
if (kubernetesCluster.getSecurityGroupId() != null &&
|
||||
networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
|
||||
List.of(kubernetesCluster.getSecurityGroupId()))) {
|
||||
networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
|
||||
List.of(kubernetesCluster.getSecurityGroupId()))) {
|
||||
List<Long> securityGroupIds = new ArrayList<>();
|
||||
securityGroupIds.add(kubernetesCluster.getSecurityGroupId());
|
||||
controlVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, securityGroupIds, owner,
|
||||
hostName, hostName, null, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, userDataId, userDataDetails, keypairs,
|
||||
hostName, hostName, null, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, userDataId, userDataDetails, keypairs,
|
||||
requestedIps, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null,
|
||||
null, true, null, UserVmManager.CKS_NODE, null, null);
|
||||
null, true, null, null, UserVmManager.CKS_NODE, null, null);
|
||||
} else {
|
||||
controlVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, owner,
|
||||
hostName, hostName, null, null, null, null,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, userDataId, userDataDetails, keypairs,
|
||||
requestedIps, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
requestedIps, addrs, null, null, affinityGroupIds, customParameterMap,
|
||||
null, null, null, null, true,
|
||||
UserVmManager.CKS_NODE, null, null, null, null);
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Created control VM: {}, {} in the Kubernetes cluster: {}", controlVm, hostName, kubernetesCluster);
|
||||
|
|
@ -372,7 +374,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
}
|
||||
|
||||
private String getEtcdNodeConfig(final List<String> ipAddresses, final List<String> hostnames, final int etcdNodeIndex,
|
||||
final boolean ejectIso) throws IOException {
|
||||
final boolean ejectIso) throws IOException {
|
||||
String k8sEtcdNodeConfig = readK8sConfigFile("/conf/etcd-node.yml");
|
||||
final String sshPubKey = "{{ k8s.ssh.pub.key }}";
|
||||
final String ejectIsoKey = "{{ k8s.eject.iso }}";
|
||||
|
|
@ -406,7 +408,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
}
|
||||
|
||||
private UserVm createKubernetesAdditionalControlNode(final String joinIp, final int additionalControlNodeInstance,
|
||||
final Long domainId, final Long accountId) throws ManagementServerException,
|
||||
final Long domainId, final Long accountId) throws ManagementServerException,
|
||||
ResourceUnavailableException, InsufficientCapacityException {
|
||||
UserVm additionalControlVm = null;
|
||||
DataCenter zone = dataCenterDao.findById(kubernetesCluster.getZoneId());
|
||||
|
|
@ -439,19 +441,21 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
|
||||
List<Long> affinityGroupIds = getMergedAffinityGroupIds(CONTROL, domainId, accountId);
|
||||
if (kubernetesCluster.getSecurityGroupId() != null &&
|
||||
networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
|
||||
List.of(kubernetesCluster.getSecurityGroupId()))) {
|
||||
networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
|
||||
List.of(kubernetesCluster.getSecurityGroupId()))) {
|
||||
List<Long> securityGroupIds = new ArrayList<>();
|
||||
securityGroupIds.add(kubernetesCluster.getSecurityGroupId());
|
||||
additionalControlVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, securityGroupIds, owner,
|
||||
hostName, hostName, null, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, null, null, keypairs,
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null,
|
||||
null, true, null, UserVmManager.CKS_NODE, null, null);
|
||||
null, true, null, null, UserVmManager.CKS_NODE, null, null);
|
||||
} else {
|
||||
additionalControlVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, owner,
|
||||
hostName, hostName, null, null, null, null,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, null, null, keypairs,
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
null, addrs, null, null, affinityGroupIds, customParameterMap,
|
||||
null, null, null, null, true,
|
||||
UserVmManager.CKS_NODE, null, null, null, null);
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
@ -488,12 +492,14 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
etcdNode = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, etcdTemplate, networkIds, securityGroupIds, owner,
|
||||
hostName, hostName, null, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, null, null, keypairs,
|
||||
Map.of(kubernetesCluster.getNetworkId(), requestedIps.get(etcdNodeIndex)), addrs, null, null, affinityGroupIds, customParameterMap, null, null, null,
|
||||
null, true, null, null, null, null);
|
||||
null, true, null, null, null, null, null);
|
||||
} else {
|
||||
etcdNode = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, etcdTemplate, networkIds, owner,
|
||||
hostName, hostName, null, null, null, null,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, null, null, keypairs,
|
||||
Map.of(kubernetesCluster.getNetworkId(), requestedIps.get(etcdNodeIndex)), addrs, null, null, affinityGroupIds, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null, null, null);
|
||||
Map.of(kubernetesCluster.getNetworkId(), requestedIps.get(etcdNodeIndex)), addrs, null, null,
|
||||
affinityGroupIds, customParameterMap, null, null, null, null,
|
||||
true, UserVmManager.CKS_NODE, null, null, null, null);
|
||||
}
|
||||
|
||||
if (logger.isInfoEnabled()) {
|
||||
|
|
@ -503,7 +509,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
}
|
||||
|
||||
private Pair<UserVm, String> provisionKubernetesClusterControlVm(final Network network, final String publicIpAddress, final List<Network.IpAddresses> etcdIps,
|
||||
final Long domainId, final Long accountId, Long asNumber) throws
|
||||
final Long domainId, final Long accountId, Long asNumber) throws
|
||||
ManagementServerException, InsufficientCapacityException, ResourceUnavailableException {
|
||||
UserVm k8sControlVM = null;
|
||||
Pair<UserVm, String> k8sControlVMAndControlIP;
|
||||
|
|
@ -525,7 +531,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
}
|
||||
|
||||
private List<UserVm> provisionKubernetesClusterAdditionalControlVms(final String controlIpAddress, final Long domainId,
|
||||
final Long accountId) throws
|
||||
final Long accountId) throws
|
||||
InsufficientCapacityException, ManagementServerException, ResourceUnavailableException {
|
||||
List<UserVm> additionalControlVms = new ArrayList<>();
|
||||
if (kubernetesCluster.getControlNodeCount() > 1) {
|
||||
|
|
@ -762,7 +768,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
}
|
||||
publicIpAddress = publicIpSshPort.first();
|
||||
if (StringUtils.isEmpty(publicIpAddress) &&
|
||||
(!manager.isDirectAccess(network) || kubernetesCluster.getControlNodeCount() > 1)) { // Shared network, single-control node cluster won't have an IP yet
|
||||
(!manager.isDirectAccess(network) || kubernetesCluster.getControlNodeCount() > 1)) { // Shared network, single-control node cluster won't have an IP yet
|
||||
logTransitStateAndThrow(Level.ERROR, String.format("Failed to start Kubernetes cluster : %s as no public IP found for the cluster" , kubernetesCluster.getName()), kubernetesCluster.getId(), KubernetesCluster.Event.CreateFailed);
|
||||
}
|
||||
// Allow account creating the kubernetes cluster to access systemVM template
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cloud-plugin-kms-database</artifactId>
|
||||
<name>Apache CloudStack Plugin - KMS Database Provider</name>
|
||||
<description>Database-backed KMS provider for encrypted key storage</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloudstack-kms-plugins</artifactId>
|
||||
<version>4.23.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-kms</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-config</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.crypto.tink</groupId>
|
||||
<artifactId>tink</artifactId>
|
||||
<version>${cs.tink.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,390 @@
|
|||
// 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.kms.provider;
|
||||
|
||||
import com.cloud.utils.component.AdapterBase;
|
||||
import com.cloud.utils.crypt.DBEncryptionUtil;
|
||||
import com.google.crypto.tink.subtle.AesGcmJce;
|
||||
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.framework.kms.KMSProvider;
|
||||
import org.apache.cloudstack.framework.kms.KeyPurpose;
|
||||
import org.apache.cloudstack.framework.kms.WrappedKey;
|
||||
import org.apache.cloudstack.kms.HSMProfileVO;
|
||||
import org.apache.cloudstack.kms.dao.HSMProfileDao;
|
||||
import org.apache.cloudstack.kms.provider.database.KMSDatabaseKekObjectVO;
|
||||
import org.apache.cloudstack.kms.provider.database.dao.KMSDatabaseKekObjectDao;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Database-backed KMS provider that stores master KEKs in a PKCS#11-like object table.
|
||||
* Uses AES-256-GCM for all cryptographic operations.
|
||||
* <p>
|
||||
* This provider is suitable for deployments that don't have access to HSM hardware.
|
||||
* The master KEKs are stored encrypted in the kms_database_kek_objects table using
|
||||
* CloudStack's existing DBEncryptionUtil, with PKCS#11-compatible attributes.
|
||||
*/
|
||||
public class DatabaseKMSProvider extends AdapterBase implements KMSProvider {
|
||||
private static final Logger logger = LogManager.getLogger(DatabaseKMSProvider.class);
|
||||
private static final String PROVIDER_NAME = "database";
|
||||
private static final int GCM_IV_LENGTH = 12; // 96 bits recommended for GCM
|
||||
private static final int GCM_TAG_LENGTH = 16; // 128 bits
|
||||
private static final String ALGORITHM = "AES/GCM/NoPadding";
|
||||
private static final String CKO_SECRET_KEY = "CKO_SECRET_KEY";
|
||||
private static final String CKK_AES = "CKK_AES";
|
||||
|
||||
private static final String DEFAULT_PROFILE_NAME = "default";
|
||||
private static final long SYSTEM_ACCOUNT_ID = 1L;
|
||||
private static final long ROOT_DOMAIN_ID = 1L;
|
||||
|
||||
private final SecureRandom secureRandom = new SecureRandom();
|
||||
@Inject
|
||||
private KMSDatabaseKekObjectDao kekObjectDao;
|
||||
@Inject
|
||||
private HSMProfileDao hsmProfileDao;
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
super.start();
|
||||
ensureDefaultHSMProfile();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderName() {
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createKek(KeyPurpose purpose, String label, int keyBits, Long hsmProfileId) throws KMSException {
|
||||
// Database provider ignores hsmProfileId
|
||||
return createKek(purpose, label, keyBits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createKek(KeyPurpose purpose, String label, int keyBits) throws KMSException {
|
||||
if (keyBits != 128 && keyBits != 192 && keyBits != 256) {
|
||||
throw KMSException.invalidParameter("Key size must be 128, 192, or 256 bits");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(label)) {
|
||||
throw KMSException.invalidParameter("KEK label cannot be empty");
|
||||
}
|
||||
|
||||
if (kekObjectDao.existsByLabel(label)) {
|
||||
throw KMSException.keyAlreadyExists("KEK with label " + label + " already exists");
|
||||
}
|
||||
|
||||
byte[] kekBytes = new byte[keyBits / 8];
|
||||
try {
|
||||
secureRandom.nextBytes(kekBytes);
|
||||
|
||||
// Base64 encode then encrypt the KEK material using DBEncryptionUtil
|
||||
String kekBase64 = Base64.getEncoder().encodeToString(kekBytes);
|
||||
String encryptedKek = DBEncryptionUtil.encrypt(kekBase64);
|
||||
byte[] encryptedKekBytes = encryptedKek.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
KMSDatabaseKekObjectVO kekObject = new KMSDatabaseKekObjectVO(label, purpose, keyBits, encryptedKekBytes);
|
||||
kekObject.setObjectClass(CKO_SECRET_KEY);
|
||||
kekObject.setKeyType(CKK_AES);
|
||||
kekObject.setObjectId(label.getBytes(StandardCharsets.UTF_8));
|
||||
kekObject.setAlgorithm(ALGORITHM);
|
||||
kekObject.setIsSensitive(true);
|
||||
kekObject.setIsExtractable(false);
|
||||
kekObject.setIsToken(true);
|
||||
kekObject.setIsPrivate(true);
|
||||
kekObject.setIsModifiable(false);
|
||||
kekObject.setIsCopyable(false);
|
||||
kekObject.setIsDestroyable(true);
|
||||
kekObject.setAlwaysSensitive(true);
|
||||
kekObject.setNeverExtractable(true);
|
||||
|
||||
kekObjectDao.persist(kekObject);
|
||||
|
||||
logger.info("Created KEK with label {} for purpose {} (PKCS#11 object ID: {})", label, purpose,
|
||||
kekObject.getId());
|
||||
return label;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw KMSException.kekOperationFailed("Failed to create KEK: " + e.getMessage(), e);
|
||||
} finally {
|
||||
Arrays.fill(kekBytes, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteKek(String kekId) throws KMSException {
|
||||
KMSDatabaseKekObjectVO kekObject = kekObjectDao.findByLabel(kekId);
|
||||
if (kekObject == null) {
|
||||
throw KMSException.kekNotFound("KEK with label " + kekId + " not found");
|
||||
}
|
||||
|
||||
try {
|
||||
kekObjectDao.remove(kekObject.getId());
|
||||
|
||||
if (kekObject.getKeyMaterial() != null) {
|
||||
Arrays.fill(kekObject.getKeyMaterial(), (byte) 0);
|
||||
}
|
||||
|
||||
logger.warn("Deleted KEK with label {}. All DEKs wrapped with this KEK are now unrecoverable!", kekId);
|
||||
} catch (Exception e) {
|
||||
throw KMSException.kekOperationFailed("Failed to delete KEK: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKekAvailable(String kekId) throws KMSException {
|
||||
try {
|
||||
KMSDatabaseKekObjectVO kekObject = kekObjectDao.findByLabel(kekId);
|
||||
return kekObject != null && kekObject.getRemoved() == null && kekObject.getKeyMaterial() != null;
|
||||
} catch (Exception e) {
|
||||
logger.warn("Error checking KEK availability: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedKey wrapKey(byte[] plainKey, KeyPurpose purpose, String kekLabel,
|
||||
Long hsmProfileId) throws KMSException {
|
||||
// Database provider ignores hsmProfileId
|
||||
return wrapKey(plainKey, purpose, kekLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedKey wrapKey(byte[] plainKey, KeyPurpose purpose, String kekLabel) throws KMSException {
|
||||
if (plainKey == null || plainKey.length == 0) {
|
||||
throw KMSException.invalidParameter("Plain key cannot be null or empty");
|
||||
}
|
||||
|
||||
byte[] kekBytes = loadKek(kekLabel);
|
||||
|
||||
try {
|
||||
// Tink's AesGcmJce automatically generates a random IV and prepends it to the ciphertext
|
||||
AesGcmJce aesgcm = new AesGcmJce(kekBytes);
|
||||
byte[] wrappedBlob = aesgcm.encrypt(plainKey, new byte[0]);
|
||||
|
||||
WrappedKey wrapped = new WrappedKey(kekLabel, purpose, ALGORITHM, wrappedBlob, PROVIDER_NAME, new Date(),
|
||||
null);
|
||||
|
||||
logger.debug("Wrapped {} key with KEK {}", purpose, kekLabel);
|
||||
return wrapped;
|
||||
} catch (Exception e) {
|
||||
throw KMSException.wrapUnwrapFailed("Failed to wrap key: " + e.getMessage(), e);
|
||||
} finally {
|
||||
// Zeroize KEK
|
||||
Arrays.fill(kekBytes, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] unwrapKey(WrappedKey wrappedKey, Long hsmProfileId) throws KMSException {
|
||||
// Database provider ignores hsmProfileId
|
||||
return unwrapKey(wrappedKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] unwrapKey(WrappedKey wrappedKey) throws KMSException {
|
||||
if (wrappedKey == null) {
|
||||
throw KMSException.invalidParameter("Wrapped key cannot be null");
|
||||
}
|
||||
|
||||
byte[] kekBytes = loadKek(wrappedKey.getKekId());
|
||||
|
||||
try {
|
||||
AesGcmJce aesgcm = new AesGcmJce(kekBytes);
|
||||
// Tink's decrypt expects [IV][ciphertext+tag] format (same as encrypt returns)
|
||||
byte[] blob = wrappedKey.getWrappedKeyMaterial();
|
||||
if (blob.length < GCM_IV_LENGTH + GCM_TAG_LENGTH) {
|
||||
throw new KMSException(KMSException.ErrorType.WRAP_UNWRAP_FAILED,
|
||||
"Invalid wrapped key format: too short");
|
||||
}
|
||||
|
||||
byte[] plainKey = aesgcm.decrypt(blob, new byte[0]);
|
||||
|
||||
logger.debug("Unwrapped {} key with KEK {}", wrappedKey.getPurpose(), wrappedKey.getKekId());
|
||||
return plainKey;
|
||||
|
||||
} catch (KMSException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw KMSException.wrapUnwrapFailed("Failed to unwrap key: " + e.getMessage(), e);
|
||||
} finally {
|
||||
// Zeroize KEK
|
||||
Arrays.fill(kekBytes, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedKey generateAndWrapDek(KeyPurpose purpose, String kekLabel, int keyBits,
|
||||
Long hsmProfileId) throws KMSException {
|
||||
// Database provider ignores hsmProfileId
|
||||
return generateAndWrapDek(purpose, kekLabel, keyBits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedKey generateAndWrapDek(KeyPurpose purpose, String kekLabel, int keyBits) throws KMSException {
|
||||
if (keyBits != 128 && keyBits != 192 && keyBits != 256) {
|
||||
throw KMSException.invalidParameter("DEK size must be 128, 192, or 256 bits");
|
||||
}
|
||||
|
||||
byte[] dekBytes = new byte[keyBits / 8];
|
||||
secureRandom.nextBytes(dekBytes);
|
||||
|
||||
try {
|
||||
return wrapKey(dekBytes, purpose, kekLabel);
|
||||
} finally {
|
||||
// Zeroize DEK (wrapped version is in WrappedKey)
|
||||
Arrays.fill(dekBytes, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedKey rewrapKey(WrappedKey oldWrappedKey, String newKekLabel,
|
||||
Long targetHsmProfileId) throws KMSException {
|
||||
// Database provider ignores targetHsmProfileId
|
||||
return rewrapKey(oldWrappedKey, newKekLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedKey rewrapKey(WrappedKey oldWrappedKey, String newKekLabel) throws KMSException {
|
||||
byte[] plainKey = unwrapKey(oldWrappedKey);
|
||||
try {
|
||||
return wrapKey(plainKey, oldWrappedKey.getPurpose(), newKekLabel);
|
||||
} finally {
|
||||
// Zeroize plaintext DEK
|
||||
Arrays.fill(plainKey, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean healthCheck() throws KMSException {
|
||||
try {
|
||||
if (kekObjectDao == null) {
|
||||
logger.error("KMSDatabaseKekObjectDao is not initialized");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw KMSException.healthCheckFailed("Health check failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] loadKek(String kekLabel) throws KMSException {
|
||||
KMSDatabaseKekObjectVO kekObject = kekObjectDao.findByLabel(kekLabel);
|
||||
|
||||
if (kekObject == null || kekObject.getRemoved() != null) {
|
||||
throw KMSException.kekNotFound("KEK with label " + kekLabel + " not found");
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] encryptedKekBytes = kekObject.getKeyMaterial();
|
||||
if (encryptedKekBytes == null || encryptedKekBytes.length == 0) {
|
||||
throw KMSException.kekNotFound("KEK value is empty for label " + kekLabel);
|
||||
}
|
||||
|
||||
String encryptedKek = new String(encryptedKekBytes, StandardCharsets.UTF_8);
|
||||
String kekBase64 = DBEncryptionUtil.decrypt(encryptedKek);
|
||||
byte[] kekBytes = Base64.getDecoder().decode(kekBase64);
|
||||
|
||||
updateLastUsed(kekLabel);
|
||||
|
||||
return kekBytes;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw KMSException.kekOperationFailed("Invalid KEK encoding for label " + kekLabel, e);
|
||||
} catch (Exception e) {
|
||||
throw KMSException.kekOperationFailed("Failed to decrypt KEK for label " + kekLabel + ": " + e.getMessage(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLastUsed(String kekLabel) {
|
||||
try {
|
||||
KMSDatabaseKekObjectVO kekObject = kekObjectDao.findByLabel(kekLabel);
|
||||
if (kekObject != null && kekObject.getRemoved() == null) {
|
||||
kekObject.setLastUsed(new Date());
|
||||
kekObjectDao.update(kekObject.getId(), kekObject);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to update last used timestamp for KEK {}: {}", kekLabel, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeds the default database HSM profile if it does not already exist.
|
||||
* This runs at provider startup to avoid FK constraint issues that occur
|
||||
* when the INSERT is placed in the schema upgrade SQL script (the account
|
||||
* table may not yet be populated when the upgrade script executes on a
|
||||
* fresh install).
|
||||
*/
|
||||
private void ensureDefaultHSMProfile() {
|
||||
try {
|
||||
SearchBuilder<HSMProfileVO> sb = hsmProfileDao.createSearchBuilder();
|
||||
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
|
||||
sb.and("system", sb.entity().getIsPublic(), SearchCriteria.Op.EQ);
|
||||
sb.and("protocol", sb.entity().getProtocol(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
|
||||
SearchCriteria<HSMProfileVO> sc = sb.create();
|
||||
sc.setParameters("name", DEFAULT_PROFILE_NAME);
|
||||
sc.setParameters("system", true);
|
||||
sc.setParameters("protocol", PROVIDER_NAME);
|
||||
|
||||
List<HSMProfileVO> existing = hsmProfileDao.customSearchIncludingRemoved(sc, null);
|
||||
if (existing != null && !existing.isEmpty()) {
|
||||
logger.debug("Default database HSM profile already exists (id={})", existing.get(0).getId());
|
||||
return;
|
||||
}
|
||||
|
||||
HSMProfileVO profile = new HSMProfileVO(DEFAULT_PROFILE_NAME, PROVIDER_NAME,
|
||||
SYSTEM_ACCOUNT_ID, ROOT_DOMAIN_ID, null, null);
|
||||
profile.setEnabled(false);
|
||||
profile.setIsPublic(true);
|
||||
hsmProfileDao.persist(profile);
|
||||
logger.info("Seeded default database HSM profile (id={}, uuid={})", profile.getId(), profile.getUuid());
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to seed default database HSM profile: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
return DatabaseKMSProvider.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,357 @@
|
|||
// 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.kms.provider.database;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.framework.kms.KeyPurpose;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Database entity for KEK objects stored by the database KMS provider.
|
||||
* Models PKCS#11 object attributes for cryptographic key storage.
|
||||
* <p>
|
||||
* This table stores KEKs (Key Encryption Keys) in a PKCS#11-compatible format,
|
||||
* allowing the database provider to mock PKCS#11 interface behavior.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "kms_database_kek_objects")
|
||||
public class KMSDatabaseKekObjectVO {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "uuid", nullable = false)
|
||||
private String uuid;
|
||||
|
||||
// PKCS#11 Object Class (CKA_CLASS)
|
||||
@Column(name = "object_class", nullable = false, length = 32)
|
||||
private String objectClass = "CKO_SECRET_KEY";
|
||||
|
||||
// PKCS#11 Label (CKA_LABEL) - human-readable identifier
|
||||
@Column(name = "label", nullable = false, length = 255)
|
||||
private String label;
|
||||
|
||||
// PKCS#11 ID (CKA_ID) - application-defined identifier
|
||||
@Column(name = "object_id", length = 64)
|
||||
private byte[] objectId;
|
||||
|
||||
// PKCS#11 Key Type (CKA_KEY_TYPE)
|
||||
@Column(name = "key_type", nullable = false, length = 32)
|
||||
private String keyType = "CKK_AES";
|
||||
|
||||
// PKCS#11 Key Value (CKA_VALUE) - encrypted KEK material
|
||||
@Column(name = "key_material", nullable = false, length = 512)
|
||||
private byte[] keyMaterial;
|
||||
|
||||
// PKCS#11 Boolean Attributes
|
||||
@Column(name = "is_sensitive", nullable = false)
|
||||
private Boolean isSensitive = true;
|
||||
|
||||
@Column(name = "is_extractable", nullable = false)
|
||||
private Boolean isExtractable = false;
|
||||
|
||||
@Column(name = "is_token", nullable = false)
|
||||
private Boolean isToken = true;
|
||||
|
||||
@Column(name = "is_private", nullable = false)
|
||||
private Boolean isPrivate = true;
|
||||
|
||||
@Column(name = "is_modifiable", nullable = false)
|
||||
private Boolean isModifiable = false;
|
||||
|
||||
@Column(name = "is_copyable", nullable = false)
|
||||
private Boolean isCopyable = false;
|
||||
|
||||
@Column(name = "is_destroyable", nullable = false)
|
||||
private Boolean isDestroyable = true;
|
||||
|
||||
@Column(name = "always_sensitive", nullable = false)
|
||||
private Boolean alwaysSensitive = true;
|
||||
|
||||
@Column(name = "never_extractable", nullable = false)
|
||||
private Boolean neverExtractable = true;
|
||||
|
||||
// Key Metadata
|
||||
@Column(name = "purpose", nullable = false, length = 32)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private KeyPurpose purpose;
|
||||
|
||||
@Column(name = "key_bits", nullable = false)
|
||||
private Integer keyBits;
|
||||
|
||||
@Column(name = "algorithm", nullable = false, length = 64)
|
||||
private String algorithm = "AES/GCM/NoPadding";
|
||||
|
||||
// PKCS#11 Validity Dates
|
||||
@Column(name = "start_date")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date startDate;
|
||||
|
||||
@Column(name = "end_date")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date endDate;
|
||||
|
||||
// Lifecycle
|
||||
@Column(name = GenericDao.CREATED_COLUMN, nullable = false)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date created;
|
||||
|
||||
@Column(name = "last_used")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date lastUsed;
|
||||
|
||||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date removed;
|
||||
|
||||
/**
|
||||
* Constructor for creating a new KEK object
|
||||
*
|
||||
* @param label PKCS#11 label (CKA_LABEL)
|
||||
* @param purpose key purpose
|
||||
* @param keyBits key size in bits
|
||||
* @param keyMaterial encrypted key material (CKA_VALUE)
|
||||
*/
|
||||
public KMSDatabaseKekObjectVO(String label, KeyPurpose purpose, Integer keyBits, byte[] keyMaterial) {
|
||||
this();
|
||||
this.label = label;
|
||||
this.purpose = purpose;
|
||||
this.keyBits = keyBits;
|
||||
this.keyMaterial = keyMaterial;
|
||||
this.objectId = label.getBytes(); // Use label as object ID by default
|
||||
this.startDate = new Date();
|
||||
}
|
||||
|
||||
public KMSDatabaseKekObjectVO() {
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
this.created = new Date();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getObjectClass() {
|
||||
return objectClass;
|
||||
}
|
||||
|
||||
public void setObjectClass(String objectClass) {
|
||||
this.objectClass = objectClass;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public byte[] getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
public void setObjectId(byte[] objectId) {
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
public String getKeyType() {
|
||||
return keyType;
|
||||
}
|
||||
|
||||
public void setKeyType(String keyType) {
|
||||
this.keyType = keyType;
|
||||
}
|
||||
|
||||
public byte[] getKeyMaterial() {
|
||||
return keyMaterial;
|
||||
}
|
||||
|
||||
public void setKeyMaterial(byte[] keyMaterial) {
|
||||
this.keyMaterial = keyMaterial;
|
||||
}
|
||||
|
||||
public Boolean getIsSensitive() {
|
||||
return isSensitive;
|
||||
}
|
||||
|
||||
public void setIsSensitive(Boolean isSensitive) {
|
||||
this.isSensitive = isSensitive;
|
||||
}
|
||||
|
||||
public Boolean getIsExtractable() {
|
||||
return isExtractable;
|
||||
}
|
||||
|
||||
public void setIsExtractable(Boolean isExtractable) {
|
||||
this.isExtractable = isExtractable;
|
||||
}
|
||||
|
||||
public Boolean getIsToken() {
|
||||
return isToken;
|
||||
}
|
||||
|
||||
public void setIsToken(Boolean isToken) {
|
||||
this.isToken = isToken;
|
||||
}
|
||||
|
||||
public Boolean getIsPrivate() {
|
||||
return isPrivate;
|
||||
}
|
||||
|
||||
public void setIsPrivate(Boolean isPrivate) {
|
||||
this.isPrivate = isPrivate;
|
||||
}
|
||||
|
||||
public Boolean getIsModifiable() {
|
||||
return isModifiable;
|
||||
}
|
||||
|
||||
public void setIsModifiable(Boolean isModifiable) {
|
||||
this.isModifiable = isModifiable;
|
||||
}
|
||||
|
||||
public Boolean getIsCopyable() {
|
||||
return isCopyable;
|
||||
}
|
||||
|
||||
public void setIsCopyable(Boolean isCopyable) {
|
||||
this.isCopyable = isCopyable;
|
||||
}
|
||||
|
||||
public Boolean getIsDestroyable() {
|
||||
return isDestroyable;
|
||||
}
|
||||
|
||||
public void setIsDestroyable(Boolean isDestroyable) {
|
||||
this.isDestroyable = isDestroyable;
|
||||
}
|
||||
|
||||
public Boolean getAlwaysSensitive() {
|
||||
return alwaysSensitive;
|
||||
}
|
||||
|
||||
public void setAlwaysSensitive(Boolean alwaysSensitive) {
|
||||
this.alwaysSensitive = alwaysSensitive;
|
||||
}
|
||||
|
||||
public Boolean getNeverExtractable() {
|
||||
return neverExtractable;
|
||||
}
|
||||
|
||||
public void setNeverExtractable(Boolean neverExtractable) {
|
||||
this.neverExtractable = neverExtractable;
|
||||
}
|
||||
|
||||
public KeyPurpose getPurpose() {
|
||||
return purpose;
|
||||
}
|
||||
|
||||
public void setPurpose(KeyPurpose purpose) {
|
||||
this.purpose = purpose;
|
||||
}
|
||||
|
||||
public Integer getKeyBits() {
|
||||
return keyBits;
|
||||
}
|
||||
|
||||
public void setKeyBits(Integer keyBits) {
|
||||
this.keyBits = keyBits;
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public void setAlgorithm(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public void setStartDate(Date startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public void setEndDate(Date endDate) {
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getLastUsed() {
|
||||
return lastUsed;
|
||||
}
|
||||
|
||||
public void setLastUsed(Date lastUsed) {
|
||||
this.lastUsed = lastUsed;
|
||||
}
|
||||
|
||||
public Date getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void setRemoved(Date removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("KMSDatabaseKekObject %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||
this, "id", "uuid", "label", "purpose", "keyBits", "objectClass", "keyType", "algorithm"));
|
||||
}
|
||||
}
|
||||
|
|
@ -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.kms.provider.database.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.framework.kms.KeyPurpose;
|
||||
import org.apache.cloudstack.kms.provider.database.KMSDatabaseKekObjectVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DAO for KMSDatabaseKekObject entities
|
||||
* Provides PKCS#11-like object storage operations for KEKs
|
||||
*/
|
||||
public interface KMSDatabaseKekObjectDao extends GenericDao<KMSDatabaseKekObjectVO, Long> {
|
||||
|
||||
/**
|
||||
* Find a KEK object by label (PKCS#11 CKA_LABEL)
|
||||
*/
|
||||
KMSDatabaseKekObjectVO findByLabel(String label);
|
||||
|
||||
/**
|
||||
* Find a KEK object by object ID (PKCS#11 CKA_ID)
|
||||
*/
|
||||
KMSDatabaseKekObjectVO findByObjectId(byte[] objectId);
|
||||
|
||||
/**
|
||||
* List all KEK objects by purpose
|
||||
*/
|
||||
List<KMSDatabaseKekObjectVO> listByPurpose(KeyPurpose purpose);
|
||||
|
||||
/**
|
||||
* List all KEK objects by key type (PKCS#11 CKA_KEY_TYPE)
|
||||
*/
|
||||
List<KMSDatabaseKekObjectVO> listByKeyType(String keyType);
|
||||
|
||||
/**
|
||||
* List all KEK objects by object class (PKCS#11 CKA_CLASS)
|
||||
*/
|
||||
List<KMSDatabaseKekObjectVO> listByObjectClass(String objectClass);
|
||||
|
||||
/**
|
||||
* Check if a KEK object exists with the given label
|
||||
*/
|
||||
boolean existsByLabel(String label);
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
// 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.kms.provider.database.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.framework.kms.KeyPurpose;
|
||||
import org.apache.cloudstack.kms.provider.database.KMSDatabaseKekObjectVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class KMSDatabaseKekObjectDaoImpl extends GenericDaoBase<KMSDatabaseKekObjectVO, Long> implements KMSDatabaseKekObjectDao {
|
||||
|
||||
private final SearchBuilder<KMSDatabaseKekObjectVO> allFieldSearch;
|
||||
|
||||
public KMSDatabaseKekObjectDaoImpl() {
|
||||
allFieldSearch = createSearchBuilder();
|
||||
allFieldSearch.and("uuid", allFieldSearch.entity().getUuid(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("label", allFieldSearch.entity().getLabel(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("objectId", allFieldSearch.entity().getObjectId(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("purpose", allFieldSearch.entity().getPurpose(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("keyType", allFieldSearch.entity().getKeyType(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.and("objectClass", allFieldSearch.entity().getObjectClass(), SearchCriteria.Op.EQ);
|
||||
allFieldSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMSDatabaseKekObjectVO findByLabel(String label) {
|
||||
SearchCriteria<KMSDatabaseKekObjectVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("label", label);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KMSDatabaseKekObjectVO findByObjectId(byte[] objectId) {
|
||||
SearchCriteria<KMSDatabaseKekObjectVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("objectId", objectId);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSDatabaseKekObjectVO> listByPurpose(KeyPurpose purpose) {
|
||||
SearchCriteria<KMSDatabaseKekObjectVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("purpose", purpose);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSDatabaseKekObjectVO> listByKeyType(String keyType) {
|
||||
SearchCriteria<KMSDatabaseKekObjectVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("keyType", keyType);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KMSDatabaseKekObjectVO> listByObjectClass(String objectClass) {
|
||||
SearchCriteria<KMSDatabaseKekObjectVO> sc = allFieldSearch.create();
|
||||
sc.setParameters("objectClass", objectClass);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByLabel(String label) {
|
||||
return findByLabel(label) != null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# 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.
|
||||
name=database-kms
|
||||
parent=kms
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns="http://www.springframework.org/schema/beans"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context.xsd"
|
||||
>
|
||||
<context:component-scan base-package="org.apache.cloudstack.kms.provider.database"/>
|
||||
<bean id="databaseKMSProvider" class="org.apache.cloudstack.kms.provider.DatabaseKMSProvider">
|
||||
<property name="name" value="DatabaseKMSProvider"/>
|
||||
</bean>
|
||||
</beans>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cloud-plugin-kms-pkcs11</artifactId>
|
||||
<name>Apache CloudStack Plugin - KMS PKCS#11 Provider</name>
|
||||
<description>PKCS#11-backed KMS provider for HSM integration</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloudstack-kms-plugins</artifactId>
|
||||
<version>4.23.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-kms</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-config</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-engine-schema</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
name=pkcs11-kms
|
||||
parent=kms
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns="http://www.springframework.org/schema/beans"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:component-scan base-package="org.apache.cloudstack.kms.provider.pkcs11"/>
|
||||
<bean id="pkcs11HSMProvider" class="org.apache.cloudstack.kms.provider.pkcs11.PKCS11HSMProvider">
|
||||
<property name="name" value="PKCS11HSMProvider"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
// 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.kms.provider.pkcs11;
|
||||
|
||||
import org.apache.cloudstack.framework.kms.KMSException;
|
||||
import org.apache.cloudstack.framework.kms.KeyPurpose;
|
||||
import org.apache.cloudstack.kms.HSMProfileDetailsVO;
|
||||
import org.apache.cloudstack.kms.KMSKekVersionVO;
|
||||
import org.apache.cloudstack.kms.dao.HSMProfileDetailsDao;
|
||||
import org.apache.cloudstack.kms.dao.KMSKekVersionDao;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Unit tests for PKCS11HSMProvider
|
||||
* Tests provider-specific logic: config loading, profile resolution, sensitive key detection
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PKCS11HSMProviderTest {
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
private PKCS11HSMProvider provider;
|
||||
|
||||
@Mock
|
||||
private HSMProfileDetailsDao hsmProfileDetailsDao;
|
||||
|
||||
@Mock
|
||||
private KMSKekVersionDao kmsKekVersionDao;
|
||||
|
||||
private Long testProfileId = 1L;
|
||||
private String testKekLabel = "test-kek-label";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// Minimal setup
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: resolveProfileId successfully finds profile from KEK label
|
||||
*/
|
||||
@Test
|
||||
public void testResolveProfileId_FindsFromKekLabel() throws KMSException {
|
||||
// Setup: KEK version with profile ID
|
||||
KMSKekVersionVO kekVersion = mock(KMSKekVersionVO.class);
|
||||
when(kekVersion.getHsmProfileId()).thenReturn(testProfileId);
|
||||
when(kmsKekVersionDao.findByKekLabel(testKekLabel)).thenReturn(kekVersion);
|
||||
|
||||
// Test
|
||||
Long result = provider.resolveProfileId(testKekLabel);
|
||||
|
||||
// Verify
|
||||
assertNotNull("Should return profile ID", result);
|
||||
assertEquals("Should return correct profile ID", testProfileId, result);
|
||||
verify(kmsKekVersionDao).findByKekLabel(testKekLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: resolveProfileId throws exception when KEK version not found
|
||||
*/
|
||||
@Test(expected = KMSException.class)
|
||||
public void testResolveProfileId_ThrowsExceptionWhenVersionNotFound() throws KMSException {
|
||||
// Setup: No KEK version found
|
||||
when(kmsKekVersionDao.findByKekLabel(testKekLabel)).thenReturn(null);
|
||||
|
||||
// Test - should throw exception
|
||||
provider.resolveProfileId(testKekLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: resolveProfileId throws exception when profile ID is null
|
||||
*/
|
||||
@Test(expected = KMSException.class)
|
||||
public void testResolveProfileId_ThrowsExceptionWhenProfileIdNull() throws KMSException {
|
||||
// Setup: KEK version exists but has null profile ID
|
||||
KMSKekVersionVO kekVersion = mock(KMSKekVersionVO.class);
|
||||
when(kekVersion.getHsmProfileId()).thenReturn(null);
|
||||
when(kmsKekVersionDao.findByKekLabel(testKekLabel)).thenReturn(kekVersion);
|
||||
|
||||
// Test - should throw exception
|
||||
provider.resolveProfileId(testKekLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: loadProfileConfig loads and decrypts sensitive values
|
||||
*/
|
||||
@Test
|
||||
public void testLoadProfileConfig_DecryptsSensitiveValues() {
|
||||
// Setup: Profile details with encrypted pin
|
||||
HSMProfileDetailsVO detail1 = mock(HSMProfileDetailsVO.class);
|
||||
when(detail1.getName()).thenReturn("library");
|
||||
when(detail1.getValue()).thenReturn("/path/to/lib.so");
|
||||
|
||||
HSMProfileDetailsVO detail2 = mock(HSMProfileDetailsVO.class);
|
||||
when(detail2.getName()).thenReturn("pin");
|
||||
when(detail2.getValue()).thenReturn("ENC(encrypted_pin)");
|
||||
|
||||
HSMProfileDetailsVO detail3 = mock(HSMProfileDetailsVO.class);
|
||||
when(detail3.getName()).thenReturn("slot");
|
||||
when(detail3.getValue()).thenReturn("0");
|
||||
|
||||
when(hsmProfileDetailsDao.listByProfileId(testProfileId)).thenReturn(
|
||||
Arrays.asList(detail1, detail2, detail3));
|
||||
|
||||
// Test
|
||||
Map<String, String> config = provider.loadProfileConfig(testProfileId);
|
||||
|
||||
// Verify
|
||||
assertNotNull("Config should not be null", config);
|
||||
assertEquals(3, config.size());
|
||||
assertEquals("/path/to/lib.so", config.get("library"));
|
||||
// Note: In real code, DBEncryptionUtil.decrypt would be called
|
||||
// Here we just verify the structure is correct
|
||||
assertTrue("Config should contain pin", config.containsKey("pin"));
|
||||
assertEquals("0", config.get("slot"));
|
||||
|
||||
verify(hsmProfileDetailsDao).listByProfileId(testProfileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: loadProfileConfig handles empty details
|
||||
*/
|
||||
@Test(expected = KMSException.class)
|
||||
public void testLoadProfileConfig_HandlesEmptyDetails() {
|
||||
// Setup
|
||||
when(hsmProfileDetailsDao.listByProfileId(testProfileId)).thenReturn(Arrays.asList());
|
||||
|
||||
// Test
|
||||
Map<String, String> config = provider.loadProfileConfig(testProfileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: isSensitiveKey correctly identifies sensitive keys
|
||||
*/
|
||||
@Test
|
||||
public void testIsSensitiveKey_IdentifiesSensitiveKeys() {
|
||||
// Test
|
||||
assertTrue(provider.isSensitiveKey("pin"));
|
||||
assertTrue(provider.isSensitiveKey("password"));
|
||||
assertTrue(provider.isSensitiveKey("api_secret"));
|
||||
assertTrue(provider.isSensitiveKey("private_key"));
|
||||
assertTrue(provider.isSensitiveKey("PIN")); // Case-insensitive
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: isSensitiveKey correctly identifies non-sensitive keys
|
||||
*/
|
||||
@Test
|
||||
public void testIsSensitiveKey_IdentifiesNonSensitiveKeys() {
|
||||
// Test
|
||||
assertFalse(provider.isSensitiveKey("library"));
|
||||
assertFalse(provider.isSensitiveKey("slot_id"));
|
||||
assertFalse(provider.isSensitiveKey("endpoint"));
|
||||
assertFalse(provider.isSensitiveKey("max_sessions"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: getProviderName returns correct name
|
||||
*/
|
||||
@Test
|
||||
public void testGetProviderName() {
|
||||
assertEquals("pkcs11", provider.getProviderName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: createKek requires hsmProfileId
|
||||
*/
|
||||
@Test(expected = KMSException.class)
|
||||
public void testCreateKek_RequiresProfileId() throws KMSException {
|
||||
provider.createKek(
|
||||
KeyPurpose.VOLUME_ENCRYPTION,
|
||||
"test-label",
|
||||
256,
|
||||
null // null profile ID should throw exception
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: getSessionPool creates pool for new profile
|
||||
*/
|
||||
@Test
|
||||
public void testGetSessionPool_CreatesPoolForNewProfile() {
|
||||
// Setup
|
||||
HSMProfileDetailsVO libraryDetail = mock(HSMProfileDetailsVO.class);
|
||||
when(libraryDetail.getName()).thenReturn("library");
|
||||
when(libraryDetail.getValue()).thenReturn("/path/to/lib.so");
|
||||
HSMProfileDetailsVO slotDetail = mock(HSMProfileDetailsVO.class);
|
||||
when(slotDetail.getName()).thenReturn("slot");
|
||||
when(slotDetail.getValue()).thenReturn("1");
|
||||
HSMProfileDetailsVO pinDetail = mock(HSMProfileDetailsVO.class);
|
||||
when(pinDetail.getName()).thenReturn("pin");
|
||||
when(pinDetail.getValue()).thenReturn("1234");
|
||||
when(hsmProfileDetailsDao.listByProfileId(testProfileId)).thenReturn(
|
||||
Arrays.asList(libraryDetail, slotDetail, pinDetail));
|
||||
|
||||
// Test
|
||||
Object pool = provider.getSessionPool(testProfileId);
|
||||
|
||||
// Verify
|
||||
assertNotNull("Pool should be created", pool);
|
||||
verify(hsmProfileDetailsDao).listByProfileId(testProfileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test: getSessionPool reuses pool for same profile
|
||||
*/
|
||||
@Test
|
||||
public void testGetSessionPool_ReusesPoolForSameProfile() {
|
||||
// Setup
|
||||
HSMProfileDetailsVO libraryDetail = mock(HSMProfileDetailsVO.class);
|
||||
when(libraryDetail.getName()).thenReturn("library");
|
||||
when(libraryDetail.getValue()).thenReturn("/path/to/lib.so");
|
||||
HSMProfileDetailsVO slotDetail = mock(HSMProfileDetailsVO.class);
|
||||
when(slotDetail.getName()).thenReturn("slot");
|
||||
when(slotDetail.getValue()).thenReturn("1");
|
||||
HSMProfileDetailsVO pinDetail = mock(HSMProfileDetailsVO.class);
|
||||
when(pinDetail.getName()).thenReturn("pin");
|
||||
when(pinDetail.getValue()).thenReturn("1234");
|
||||
when(hsmProfileDetailsDao.listByProfileId(testProfileId)).thenReturn(
|
||||
Arrays.asList(libraryDetail, slotDetail, pinDetail));
|
||||
|
||||
// Test
|
||||
Object pool1 = provider.getSessionPool(testProfileId);
|
||||
Object pool2 = provider.getSessionPool(testProfileId);
|
||||
|
||||
// Verify
|
||||
assertNotNull("Pool should be created", pool1);
|
||||
assertEquals("Should reuse same pool", pool1, pool2);
|
||||
// Config should only be loaded once
|
||||
verify(hsmProfileDetailsDao, times(1)).listByProfileId(testProfileId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>cloudstack-kms-plugins</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Apache CloudStack Plugin - KMS</name>
|
||||
<description>Key Management Service providers</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloudstack-plugins</artifactId>
|
||||
<version>4.23.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>database</module>
|
||||
<module>pkcs11</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
@ -95,6 +95,8 @@
|
|||
<module>integrations/prometheus</module>
|
||||
<module>integrations/kubernetes-service</module>
|
||||
|
||||
<module>kms</module>
|
||||
|
||||
<module>metrics</module>
|
||||
|
||||
<module>network-elements/bigswitch</module>
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ public class StorageVmSharedFSLifeCycle implements SharedFSLifeCycle {
|
|||
diskOfferingId, size, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData,
|
||||
null, null, keypairs, null, addrs, null, null, null,
|
||||
customParameterMap, null, null, null, null,
|
||||
true, UserVmManager.SHAREDFSVM, null, null, null);
|
||||
true, UserVmManager.SHAREDFSVM, null, null, null, null);
|
||||
vmContext.setEventResourceId(vm.getId());
|
||||
userVmService.startVirtualMachine(vm, null);
|
||||
} catch (InsufficientCapacityException ex) {
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ public class StorageVmSharedFSLifeCycleTest {
|
|||
anyString(), anyLong(), anyLong(), any(), isNull(), any(Hypervisor.HypervisorType.class), any(BaseCmd.HTTPMethod.class), anyString(),
|
||||
isNull(), isNull(), anyList(), isNull(), any(Network.IpAddresses.class), isNull(), isNull(), isNull(),
|
||||
anyMap(), isNull(), isNull(), isNull(), isNull(),
|
||||
anyBoolean(), anyString(), isNull(), isNull(), isNull())).thenReturn(vm);
|
||||
anyBoolean(), anyString(), isNull(), isNull(), isNull(), isNull())).thenReturn(vm);
|
||||
|
||||
VolumeVO rootVol = mock(VolumeVO.class);
|
||||
when(rootVol.getVolumeType()).thenReturn(Volume.Type.ROOT);
|
||||
|
|
|
|||
|
|
@ -576,11 +576,25 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
|
|||
*/
|
||||
private boolean anyVolumeRequiresEncryption(DataObject ... objects) {
|
||||
for (DataObject o : objects) {
|
||||
// this fails code smell for returning true twice, but it is more readable than combining all tests into one statement
|
||||
if (o instanceof VolumeInfo && ((VolumeInfo) o).getPassphraseId() != null) {
|
||||
return true;
|
||||
} else if (o instanceof SnapshotInfo && ((SnapshotInfo) o).getBaseVolume().getPassphraseId() != null) {
|
||||
return true;
|
||||
// Check for legacy passphrase-based encryption
|
||||
if (o instanceof VolumeInfo) {
|
||||
VolumeInfo vol = (VolumeInfo) o;
|
||||
if (vol.getPassphraseId() != null) {
|
||||
return true;
|
||||
}
|
||||
// Check for KMS-based encryption
|
||||
if (vol.getKmsWrappedKeyId() != null || vol.getKmsKeyId() != null) {
|
||||
return true;
|
||||
}
|
||||
} else if (o instanceof SnapshotInfo) {
|
||||
VolumeInfo baseVol = ((SnapshotInfo) o).getBaseVolume();
|
||||
if (baseVol.getPassphraseId() != null) {
|
||||
return true;
|
||||
}
|
||||
// Check for KMS-based encryption
|
||||
if (baseVol.getKmsWrappedKeyId() != null || baseVol.getKmsKeyId() != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,11 @@
|
|||
<artifactId>cloud-framework-ca</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-kms</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-jobs</artifactId>
|
||||
|
|
|
|||
|
|
@ -2666,6 +2666,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||
Long clusterId = cmd.getClusterId();
|
||||
Long serviceOfferingId = cmd.getServiceOfferingId();
|
||||
Long diskOfferingId = cmd.getDiskOfferingId();
|
||||
Long kmsKeyId = cmd.getKmsKeyId();
|
||||
Boolean display = cmd.getDisplay();
|
||||
String state = cmd.getState();
|
||||
boolean shouldListSystemVms = shouldListSystemVms(cmd, caller.getId());
|
||||
|
|
@ -2702,6 +2703,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||
volumeSearchBuilder.and("uuid", volumeSearchBuilder.entity().getUuid(), SearchCriteria.Op.NNULL);
|
||||
volumeSearchBuilder.and("instanceId", volumeSearchBuilder.entity().getInstanceId(), SearchCriteria.Op.EQ);
|
||||
volumeSearchBuilder.and("dataCenterId", volumeSearchBuilder.entity().getDataCenterId(), SearchCriteria.Op.EQ);
|
||||
volumeSearchBuilder.and("kmsKeyId", volumeSearchBuilder.entity().getKmsKeyId(), SearchCriteria.Op.EQ);
|
||||
if (cmd.isEncrypted() != null) {
|
||||
if (cmd.isEncrypted()) {
|
||||
volumeSearchBuilder.and("encryptFormat", volumeSearchBuilder.entity().getEncryptFormat(), SearchCriteria.Op.NNULL);
|
||||
|
|
@ -2826,6 +2828,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||
if (vmInstanceId != null) {
|
||||
sc.setParameters("instanceId", vmInstanceId);
|
||||
}
|
||||
if (kmsKeyId != null) {
|
||||
sc.setParameters("kmsKeyId", kmsKeyId);
|
||||
}
|
||||
if (zoneId != null) {
|
||||
sc.setParameters("dataCenterId", zoneId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// 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
|
||||
|
|
@ -29,6 +29,10 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
|||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.kms.KMSKekVersionVO;
|
||||
import org.apache.cloudstack.kms.KMSWrappedKeyVO;
|
||||
import org.apache.cloudstack.kms.dao.KMSKekVersionDao;
|
||||
import org.apache.cloudstack.kms.dao.KMSWrappedKeyDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -58,6 +62,10 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation<VolumeJo
|
|||
@Inject
|
||||
private PrimaryDataStoreDao primaryDataStoreDao;
|
||||
@Inject
|
||||
private KMSWrappedKeyDao kmsWrappedKeyDao;
|
||||
@Inject
|
||||
private KMSKekVersionDao kmsKekVersionDao;
|
||||
@Inject
|
||||
private AnnotationDao annotationDao;
|
||||
|
||||
private final SearchBuilder<VolumeJoinVO> volSearch;
|
||||
|
|
@ -284,6 +292,18 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation<VolumeJo
|
|||
volResponse.setObjectName("volume");
|
||||
volResponse.setExternalUuid(volume.getExternalUuid());
|
||||
volResponse.setEncryptionFormat(volume.getEncryptionFormat());
|
||||
volResponse.setKmsKeyId(volume.getKmsKeyUuid());
|
||||
volResponse.setKmsKey(volume.getKmsKeyName());
|
||||
|
||||
if (volume.getKmsWrappedKeyId() != null) {
|
||||
KMSWrappedKeyVO wrappedKey = kmsWrappedKeyDao.findById(volume.getKmsWrappedKeyId());
|
||||
if (wrappedKey != null) {
|
||||
KMSKekVersionVO kekVersion = kmsKekVersionDao.findById(wrappedKey.getKekVersionId());
|
||||
if (kekVersion != null) {
|
||||
volResponse.setKmsKeyVersion(kekVersion.getVersionNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
return volResponse;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -280,6 +280,18 @@ public class VolumeJoinVO extends BaseViewWithTagInformationVO implements Contro
|
|||
@Column(name = "encrypt_format")
|
||||
private String encryptionFormat = null;
|
||||
|
||||
@Column(name = "kms_key_id")
|
||||
private Long kmsKeyId;
|
||||
|
||||
@Column(name = "kms_key_uuid")
|
||||
private String kmsKeyUuid;
|
||||
|
||||
@Column(name = "kms_key_name")
|
||||
private String kmsKeyName;
|
||||
|
||||
@Column(name = "kms_wrapped_key_id")
|
||||
private Long kmsWrappedKeyId;
|
||||
|
||||
@Column(name = "delete_protection")
|
||||
protected Boolean deleteProtection;
|
||||
|
||||
|
|
@ -622,6 +634,22 @@ public class VolumeJoinVO extends BaseViewWithTagInformationVO implements Contro
|
|||
return encryptionFormat;
|
||||
}
|
||||
|
||||
public Long getKmsKeyId() {
|
||||
return kmsKeyId;
|
||||
}
|
||||
|
||||
public String getKmsKeyName() {
|
||||
return kmsKeyName;
|
||||
}
|
||||
|
||||
public String getKmsKeyUuid() {
|
||||
return kmsKeyUuid;
|
||||
}
|
||||
|
||||
public Long getKmsWrappedKeyId() {
|
||||
return kmsWrappedKeyId;
|
||||
}
|
||||
|
||||
public Boolean getDeleteProtection() {
|
||||
return deleteProtection;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1833,7 +1833,7 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
|
|||
vmDisplayName, diskOfferingId, dataDiskSize, null, null,
|
||||
hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs,
|
||||
null, null, true, null, affinityGroupIdList, customParameters, null, null, null,
|
||||
null, true, overrideDiskOfferingId, null, null);
|
||||
null, true, overrideDiskOfferingId, null, null, null);
|
||||
} else {
|
||||
if (networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
|
||||
Collections.emptyList())) {
|
||||
|
|
@ -1841,13 +1841,13 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScaleManage
|
|||
owner, vmHostName, vmDisplayName, diskOfferingId, dataDiskSize, null, null,
|
||||
hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs,
|
||||
null, null, true, null, affinityGroupIdList, customParameters, null, null, null,
|
||||
null, true, overrideDiskOfferingId, null, null, null);
|
||||
null, true, overrideDiskOfferingId, null, null, null, null);
|
||||
} else {
|
||||
vm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, vmHostName, vmDisplayName,
|
||||
diskOfferingId, dataDiskSize, null, null,
|
||||
hypervisorType, HTTPMethod.GET, userData, userDataId, userDataDetails, sshKeyPairs,
|
||||
null, addrs, true, null, affinityGroupIdList, customParameters, null, null, null,
|
||||
null, true, null, overrideDiskOfferingId, null, null);
|
||||
null, true, null, overrideDiskOfferingId, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO;
|
|||
import org.apache.cloudstack.resourcedetail.SnapshotPolicyDetailVO;
|
||||
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
|
||||
import org.apache.cloudstack.resourcedetail.dao.SnapshotPolicyDetailsDao;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
import org.apache.cloudstack.resourcelimit.Reserver;
|
||||
import org.apache.cloudstack.snapshot.SnapshotHelper;
|
||||
import org.apache.cloudstack.storage.command.AttachAnswer;
|
||||
|
|
@ -374,6 +375,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
private ReservationDao reservationDao;
|
||||
@Inject
|
||||
private VMSnapshotDetailsDao vmSnapshotDetailsDao;
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
public static final String KVM_FILE_BASED_STORAGE_SNAPSHOT = "kvmFileBasedStorageSnapshot";
|
||||
|
||||
|
|
@ -985,8 +988,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
String userSpecifiedName = getVolumeNameFromCommand(cmd);
|
||||
|
||||
if (cmd.getKmsKeyId() != null) {
|
||||
kmsManager.checkKmsKeyForVolumeEncryption(caller, cmd.getKmsKeyId(), zoneId);
|
||||
}
|
||||
|
||||
return commitVolume(cmd.getSnapshotId(), caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName,
|
||||
_uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details);
|
||||
_uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details, cmd.getKmsKeyId());
|
||||
} finally {
|
||||
ReservationHelper.closeAll(reservations);
|
||||
}
|
||||
|
|
@ -1003,7 +1010,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
}
|
||||
|
||||
private VolumeVO commitVolume(final Long snapshotId, final Account caller, final Account owner, final Boolean displayVolume, final Long zoneId, final Long diskOfferingId,
|
||||
final Storage.ProvisioningType provisioningType, final Long size, final Long minIops, final Long maxIops, final VolumeVO parentVolume, final String userSpecifiedName, final String uuid, final Map<String, String> details) {
|
||||
final Storage.ProvisioningType provisioningType, final Long size, final Long minIops, final Long maxIops, final VolumeVO parentVolume, final String userSpecifiedName, final String uuid, final Map<String, String> details, final Long kmsKeyId) {
|
||||
return Transaction.execute(new TransactionCallback<VolumeVO>() {
|
||||
@Override
|
||||
public VolumeVO doInTransaction(TransactionStatus status) {
|
||||
|
|
@ -1049,6 +1056,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
}
|
||||
}
|
||||
|
||||
// Store KMS key ID if provided (for volume encryption)
|
||||
if (volume != null && kmsKeyId != null) {
|
||||
volume.setKmsKeyId(kmsKeyId);
|
||||
_volsDao.update(volume.getId(), volume);
|
||||
}
|
||||
|
||||
CallContext.current().setEventDetails("Volume ID: " + volume.getUuid());
|
||||
CallContext.current().putContextParameter(Volume.class, volume.getId());
|
||||
// Increment resource count during allocation; if actual creation fails,
|
||||
|
|
@ -2796,7 +2809,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
}
|
||||
|
||||
DiskOfferingVO diskOffering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId());
|
||||
if (diskOffering.getEncrypt() && rootDiskHyperType != HypervisorType.KVM) {
|
||||
if (diskOffering.getEncrypt() && !(rootDiskHyperType == HypervisorType.KVM)) {
|
||||
throw new InvalidParameterValueException("Volume's disk offering has encryption enabled, but volume encryption is not supported for hypervisor type " + rootDiskHyperType);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
|
|||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
||||
import org.apache.cloudstack.framework.messagebus.PublishScope;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||
import org.apache.cloudstack.query.QueryService;
|
||||
import org.apache.cloudstack.network.RoutedIpv4Manager;
|
||||
|
|
@ -349,6 +350,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
private NetworkPermissionDao networkPermissionDao;
|
||||
@Inject
|
||||
private SslCertDao sslCertDao;
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
|
||||
private List<QuerySelector> _querySelectors;
|
||||
|
||||
|
|
@ -1249,6 +1252,17 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
// Delete Webhooks
|
||||
deleteWebhooksForAccount(accountId);
|
||||
|
||||
// Delete KMS keys
|
||||
try {
|
||||
if (!kmsManager.deleteKMSKeysByAccountId(accountId)) {
|
||||
logger.warn("Failed to delete all KMS keys for account {}", account);
|
||||
accountCleanupNeeded = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Error deleting KMS keys for account {}: {}", account, e.getMessage(), e);
|
||||
accountCleanupNeeded = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Failed to cleanup account " + account + " due to ", ex);
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ import org.apache.cloudstack.backup.BackupVO;
|
|||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupScheduleDao;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity;
|
||||
import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMNetworkMapDao;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
|
|
@ -479,6 +480,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
@Inject
|
||||
private AccountManager _accountMgr;
|
||||
@Inject
|
||||
private KMSManager kmsManager;
|
||||
@Inject
|
||||
private AccountService _accountService;
|
||||
@Inject
|
||||
private ClusterDao _clusterDao;
|
||||
|
|
@ -3859,7 +3862,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List<VmDiskInfo> dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod,
|
||||
String userData, Long userDataId, String userDataDetails, List<String> sshKeyPairs, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List<Long> affinityGroupIdList,
|
||||
Map<String, String> customParametes, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
|
||||
StorageUnavailableException, ResourceAllocationException {
|
||||
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
|
@ -3908,7 +3911,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, dataDiskInfoList, networkList, securityGroupIdList, group, httpmethod,
|
||||
userData, userDataId, userDataDetails, sshKeyPairs, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId, dhcpOptionMap,
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null, overrideDiskOfferingId, volume, snapshot);
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null, overrideDiskOfferingId, rootDiskKmsKeyId, volume, snapshot);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -3918,7 +3921,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List<VmDiskInfo> dataDiskInfoList, String group, HypervisorType hypervisor,
|
||||
HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List<String> sshKeyPairs, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
|
||||
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, String vmType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException {
|
||||
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
List<NetworkVO> networkList = new ArrayList<>();
|
||||
|
|
@ -4021,7 +4024,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, dataDiskInfoList, networkList, securityGroupIdList, group, httpmethod,
|
||||
userData, userDataId, userDataDetails, sshKeyPairs, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, dataDiskTemplateToDiskOfferingMap,
|
||||
userVmOVFProperties, dynamicScalingEnabled, vmType, overrideDiskOfferingId, volume, snapshot);
|
||||
userVmOVFProperties, dynamicScalingEnabled, vmType, overrideDiskOfferingId, rootDiskKmsKeyId, volume, snapshot);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -4030,7 +4033,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
String hostName, String displayName, Long diskOfferingId, Long diskSize, List<VmDiskInfo> dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
|
||||
Long userDataId, String userDataDetails, List<String> sshKeyPairs, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayvm, String keyboard, List<Long> affinityGroupIdList,
|
||||
Map<String, String> customParametrs, String customId, Map<String, Map<Integer, String>> dhcpOptionsMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Map<String, String> userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
|
||||
Map<String, String> userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
|
||||
StorageUnavailableException, ResourceAllocationException {
|
||||
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
|
@ -4083,7 +4086,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
verifyExtraDhcpOptionsNetwork(dhcpOptionsMap, networkList);
|
||||
return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, dataDiskInfoList, networkList, null, group, httpmethod, userData,
|
||||
userDataId, userDataDetails, sshKeyPairs, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId, dhcpOptionsMap,
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, overrideDiskOfferingId, volume, snapshot);
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, overrideDiskOfferingId, rootDiskKmsKeyId, volume, snapshot);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -4215,7 +4218,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
Long userDataId, String userDataDetails, List<String> sshKeyPairs, HypervisorType hypervisor, Account caller, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean isDisplayVm, String keyboard,
|
||||
List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
|
||||
Map<Long, DiskOffering> datadiskTemplateToDiskOfferringMap,
|
||||
Map<String, String> userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ResourceUnavailableException,
|
||||
Map<String, String> userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Long rootDiskKmsKeyId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ResourceUnavailableException,
|
||||
ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException {
|
||||
|
||||
_accountMgr.checkAccess(caller, null, true, owner);
|
||||
|
|
@ -4302,7 +4305,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
throw new InvalidParameterValueException("Root volume encryption is not supported for hypervisor type " + hypervisorType);
|
||||
}
|
||||
|
||||
UserVm vm = getCheckedUserVmResource(zone, hostName, displayName, owner, diskOfferingId, diskSize, dataDiskInfoList, networkList, securityGroupIdList, group, httpmethod, userData, userDataId, userDataDetails, sshKeyPairs, caller, requestedIps, defaultIps, isDisplayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, template, hypervisorType, accountId, offering, isIso, rootDiskOfferingId, volumesSize, volume, snapshot);
|
||||
kmsManager.checkKmsKeyForVolumeEncryption(caller, rootDiskKmsKeyId, zone.getId());
|
||||
if (dataDiskInfoList != null) {
|
||||
for (VmDiskInfo diskInfo : dataDiskInfoList) {
|
||||
kmsManager.checkKmsKeyForVolumeEncryption(caller, diskInfo.getKmsKeyId(), zone.getId());
|
||||
}
|
||||
}
|
||||
|
||||
UserVm vm = getCheckedUserVmResource(zone, hostName, displayName, owner, diskOfferingId, diskSize, dataDiskInfoList, networkList, securityGroupIdList, group, httpmethod, userData, userDataId, userDataDetails, sshKeyPairs, caller, requestedIps, defaultIps, isDisplayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, template, hypervisorType, accountId, offering, isIso, rootDiskOfferingId, rootDiskKmsKeyId, volumesSize, volume, snapshot);
|
||||
|
||||
_securityGroupMgr.addInstanceToGroups(vm, securityGroupIdList);
|
||||
|
||||
|
|
@ -4322,7 +4332,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
Map<String, Map<Integer, String>> dhcpOptionMap, Map<Long, DiskOffering> datadiskTemplateToDiskOfferringMap,
|
||||
Map<String, String> userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String vmType, VMTemplateVO template,
|
||||
HypervisorType hypervisorType, long accountId, ServiceOfferingVO offering, boolean isIso,
|
||||
Long rootDiskOfferingId, long volumesSize, Volume volume, Snapshot snapshot) throws ResourceAllocationException {
|
||||
Long rootDiskOfferingId, Long rootDiskKmsKeyId, long volumesSize, Volume volume, Snapshot snapshot) throws ResourceAllocationException {
|
||||
if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) {
|
||||
List<String> resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(offering, template);
|
||||
try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, resourceLimitHostTags, 1l, reservationDao, resourceLimitService);
|
||||
|
|
@ -4331,7 +4341,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
CheckedReservation gpuReservation = offering.getGpuCount() != null && offering.getGpuCount() > 0 ?
|
||||
new CheckedReservation(owner, ResourceType.gpu, resourceLimitHostTags, Long.valueOf(offering.getGpuCount()), reservationDao, resourceLimitService) : null;
|
||||
) {
|
||||
return getUncheckedUserVmResource(zone, hostName, displayName, owner, diskOfferingId, diskSize, dataDiskInfoList, networkList, securityGroupIdList, group, httpmethod, userData, userDataId, userDataDetails, sshKeyPairs, caller, requestedIps, defaultIps, isDisplayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, template, hypervisorType, accountId, offering, isIso, rootDiskOfferingId, volumesSize, volume, snapshot);
|
||||
return getUncheckedUserVmResource(zone, hostName, displayName, owner, diskOfferingId, diskSize, dataDiskInfoList, networkList, securityGroupIdList, group, httpmethod, userData, userDataId, userDataDetails, sshKeyPairs, caller, requestedIps, defaultIps, isDisplayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, template, hypervisorType, accountId, offering, isIso, rootDiskOfferingId, rootDiskKmsKeyId, volumesSize, volume, snapshot);
|
||||
} catch (ResourceAllocationException | CloudRuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
|
|
@ -4340,7 +4350,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
|
||||
} else {
|
||||
return getUncheckedUserVmResource(zone, hostName, displayName, owner, diskOfferingId, diskSize, dataDiskInfoList, networkList, securityGroupIdList, group, httpmethod, userData, userDataId, userDataDetails, sshKeyPairs, caller, requestedIps, defaultIps, isDisplayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, template, hypervisorType, accountId, offering, isIso, rootDiskOfferingId, volumesSize, volume, snapshot);
|
||||
return getUncheckedUserVmResource(zone, hostName, displayName, owner, diskOfferingId, diskSize, dataDiskInfoList, networkList, securityGroupIdList, group, httpmethod, userData, userDataId, userDataDetails, sshKeyPairs, caller, requestedIps, defaultIps, isDisplayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, template, hypervisorType, accountId, offering, isIso, rootDiskOfferingId, rootDiskKmsKeyId, volumesSize, volume, snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4388,9 +4398,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
Map<String, Map<Integer, String>> dhcpOptionMap, Map<Long, DiskOffering> datadiskTemplateToDiskOfferringMap,
|
||||
Map<String, String> userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String vmType, VMTemplateVO template,
|
||||
HypervisorType hypervisorType, long accountId, ServiceOfferingVO offering, boolean isIso,
|
||||
Long rootDiskOfferingId, long volumesSize, Volume volume, Snapshot snapshot) throws ResourceAllocationException {
|
||||
Long rootDiskOfferingId, Long rootDiskKmsKeyId, long volumesSize, Volume volume, Snapshot snapshot) throws ResourceAllocationException {
|
||||
List<Reserver> checkedReservations = new ArrayList<>();
|
||||
|
||||
|
||||
try {
|
||||
reserveStorageResourcesForVm(checkedReservations, owner, diskOfferingId, diskSize, dataDiskInfoList, rootDiskOfferingId, offering, volumesSize);
|
||||
|
||||
|
|
@ -4673,7 +4684,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
UserVmVO vm = commitUserVm(zone, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, userDataId, userDataDetails, caller, isDisplayVm, keyboard, accountId, userId, offering,
|
||||
isIso, sshPublicKeys, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters, dhcpOptionMap,
|
||||
datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, rootDiskOfferingId, keypairnames, dataDiskInfoList, volume, snapshot);
|
||||
datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, vmType, rootDiskOfferingId, rootDiskKmsKeyId, keypairnames, dataDiskInfoList, volume, snapshot);
|
||||
|
||||
assignInstanceToGroup(group, id);
|
||||
return vm;
|
||||
|
|
@ -4868,7 +4879,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
final long accountId, final long userId, final ServiceOffering offering, final boolean isIso, final Long guestOsId, final String sshPublicKeys, final LinkedHashMap<String, List<NicProfile>> networkNicMap,
|
||||
final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map<String, String> customParameters,
|
||||
final Map<String, Map<Integer, String>> extraDhcpOptionMap, final Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
final Map<String, String> userVmOVFPropertiesMap, final VirtualMachine.PowerState powerState, final boolean dynamicScalingEnabled, String vmType, final Long rootDiskOfferingId, String sshkeypairs,
|
||||
final Map<String, String> userVmOVFPropertiesMap, final VirtualMachine.PowerState powerState, final boolean dynamicScalingEnabled, String vmType, final Long rootDiskOfferingId, final Long rootDiskKmsKeyId, String sshkeypairs,
|
||||
List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException {
|
||||
Long selectedGuestOsId = guestOsId != null ? guestOsId : template.getGuestOSId();
|
||||
UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, selectedGuestOsId, offering.isOfferHA(),
|
||||
|
|
@ -4987,7 +4998,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
orchestrateVirtualMachineCreate(vm, guestOSCategory, computeTags, rootDiskTags, plan, rootDiskSize, template, hostName, displayName, owner,
|
||||
diskOfferingId, diskSize, offering, isIso,networkNicMap, hypervisorType, extraDhcpOptionMap, dataDiskTemplateToDiskOfferingMap,
|
||||
rootDiskOfferingId, dataDiskInfoList, volume, snapshot);
|
||||
rootDiskOfferingId, rootDiskKmsKeyId, dataDiskInfoList, volume, snapshot);
|
||||
|
||||
}
|
||||
CallContext.current().setEventDetails("Vm Id: " + vm.getUuid());
|
||||
|
|
@ -5020,16 +5031,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
ServiceOffering offering, boolean isIso, LinkedHashMap<String, List<NicProfile>> networkNicMap,
|
||||
HypervisorType hypervisorType,
|
||||
Map<String, Map<Integer, String>> extraDhcpOptionMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Long rootDiskOfferingId, List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException{
|
||||
Long rootDiskOfferingId, Long rootDiskKmsKeyId, List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException{
|
||||
try {
|
||||
if (isIso) {
|
||||
_orchSrvc.createVirtualMachineFromScratch(vm.getUuid(), Long.toString(owner.getAccountId()), vm.getIsoId().toString(), hostName, displayName,
|
||||
hypervisorType.name(), guestOSCategory.getName(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags,
|
||||
networkNicMap, plan, extraDhcpOptionMap, rootDiskOfferingId, dataDiskInfoList, volume, snapshot);
|
||||
networkNicMap, plan, extraDhcpOptionMap, rootDiskOfferingId, rootDiskKmsKeyId, dataDiskInfoList, volume, snapshot);
|
||||
} else {
|
||||
_orchSrvc.createVirtualMachine(vm.getUuid(), Long.toString(owner.getAccountId()), Long.toString(template.getId()), hostName, displayName, hypervisorType.name(),
|
||||
offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkNicMap, plan, rootDiskSize, extraDhcpOptionMap,
|
||||
dataDiskTemplateToDiskOfferingMap, diskOfferingId, rootDiskOfferingId, dataDiskInfoList, volume, snapshot);
|
||||
dataDiskTemplateToDiskOfferingMap, diskOfferingId, rootDiskOfferingId, rootDiskKmsKeyId, dataDiskInfoList, volume, snapshot);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
|
@ -5151,14 +5162,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
final long accountId, final long userId, final ServiceOfferingVO offering, final boolean isIso, final String sshPublicKeys, final LinkedHashMap<String, List<NicProfile>> networkNicMap,
|
||||
final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map<String, String> customParameters, final Map<String,
|
||||
Map<Integer, String>> extraDhcpOptionMap, final Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
|
||||
Map<String, String> userVmOVFPropertiesMap, final boolean dynamicScalingEnabled, String vmType, final Long rootDiskOfferingId, String sshkeypairs,
|
||||
Map<String, String> userVmOVFPropertiesMap, final boolean dynamicScalingEnabled, String vmType, final Long rootDiskOfferingId, final Long rootDiskKmsKeyId, String sshkeypairs,
|
||||
List<VmDiskInfo> dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException {
|
||||
return commitUserVm(false, zone, null, null, template, hostName, displayName, owner,
|
||||
diskOfferingId, diskSize, userData, userDataId, userDataDetails, isDisplayVm, keyboard,
|
||||
accountId, userId, offering, isIso, null, sshPublicKeys, networkNicMap,
|
||||
id, instanceName, uuidName, hypervisorType, customParameters,
|
||||
extraDhcpOptionMap, dataDiskTemplateToDiskOfferingMap,
|
||||
userVmOVFPropertiesMap, null, dynamicScalingEnabled, vmType, rootDiskOfferingId, sshkeypairs, dataDiskInfoList, volume, snapshot);
|
||||
userVmOVFPropertiesMap, null, dynamicScalingEnabled, vmType, rootDiskOfferingId, rootDiskKmsKeyId, sshkeypairs, dataDiskInfoList, volume, snapshot);
|
||||
}
|
||||
|
||||
public void validateRootDiskResize(final HypervisorType hypervisorType, Long rootDiskSize, VMTemplateVO templateVO, UserVmVO vm, final Map<String, String> customParameters) throws InvalidParameterValueException
|
||||
|
|
@ -6575,7 +6586,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
vm = createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(cmd, zone, template, owner), owner, name, displayName, diskOfferingId,
|
||||
size , dataDiskInfoList, group , hypervisor, cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, ipToNetworkMap, addrs, displayVm , keyboard , cmd.getAffinityGroupIdList(),
|
||||
cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(),
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId, volume, snapshot);
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId, cmd.getRootDiskKmsKeyId(), volume, snapshot);
|
||||
}
|
||||
} else {
|
||||
if (_networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
|
||||
|
|
@ -6583,7 +6594,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
vm = createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, networkIds, getSecurityGroupIdList(cmd, zone, template, owner), owner, name,
|
||||
displayName, diskOfferingId, size, dataDiskInfoList, group, hypervisor, cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, ipToNetworkMap, addrs, displayVm, keyboard,
|
||||
cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(),
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId, null, volume, snapshot);
|
||||
dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId, cmd.getRootDiskKmsKeyId(), null, volume, snapshot);
|
||||
|
||||
} else {
|
||||
if (cmd.getSecurityGroupIdList() != null && !cmd.getSecurityGroupIdList().isEmpty()) {
|
||||
|
|
@ -6591,7 +6602,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
vm = createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, name, displayName, diskOfferingId, size, dataDiskInfoList, group,
|
||||
hypervisor, cmd.getHttpMethod(), userData, userDataId, userDataDetails, sshKeyPairNames, ipToNetworkMap, addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(),
|
||||
cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null, overrideDiskOfferingId, volume, snapshot);
|
||||
cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null, overrideDiskOfferingId, cmd.getRootDiskKmsKeyId(), volume, snapshot);
|
||||
if (cmd instanceof DeployVnfApplianceCmd) {
|
||||
vnfTemplateManager.createIsolatedNetworkRulesForVnfAppliance(zone, template, owner, vm, (DeployVnfApplianceCmd) cmd);
|
||||
}
|
||||
|
|
@ -9625,7 +9636,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
null, null, userData, null, null, isDisplayVm, keyboard,
|
||||
accountId, userId, serviceOffering, template.getFormat().equals(ImageFormat.ISO), guestOsId, sshPublicKeys, networkNicMap,
|
||||
id, instanceName, uuidName, hypervisorType, customParameters,
|
||||
null, null, null, powerState, dynamicScalingEnabled, null, serviceOffering.getDiskOfferingId(), null, null, null, null);
|
||||
null, null, null, powerState, dynamicScalingEnabled, null, serviceOffering.getDiskOfferingId(), null, null, null, null, null);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -749,8 +749,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
}
|
||||
|
||||
private Pair<DiskProfile, StoragePool> importExternalDisk(UnmanagedInstanceTO.Disk disk, VirtualMachine vm, DeployDestination dest, DiskOffering diskOffering,
|
||||
Volume.Type type, VirtualMachineTemplate template,Long deviceId, String remoteUrl, String username, String password,
|
||||
String tmpPath, DiskProfile diskProfile) {
|
||||
Volume.Type type, VirtualMachineTemplate template,Long deviceId, String remoteUrl, String username, String password,
|
||||
String tmpPath, DiskProfile diskProfile) {
|
||||
final String path = StringUtils.isEmpty(disk.getDatastorePath()) ? disk.getImagePath() : disk.getDatastorePath();
|
||||
String chainInfo = disk.getChainInfo();
|
||||
if (StringUtils.isEmpty(chainInfo)) {
|
||||
|
|
@ -818,8 +818,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
}
|
||||
|
||||
private Pair<DiskProfile, StoragePool> importKVMSharedDisk(VirtualMachine vm, DiskOffering diskOffering,
|
||||
Volume.Type type, VirtualMachineTemplate template,
|
||||
Long deviceId, Long poolId, String diskPath, DiskProfile diskProfile) {
|
||||
Volume.Type type, VirtualMachineTemplate template,
|
||||
Long deviceId, Long poolId, String diskPath, DiskProfile diskProfile) {
|
||||
StoragePool storagePool = primaryDataStoreDao.findById(poolId);
|
||||
|
||||
DiskProfile profile = volumeManager.updateImportedVolume(type, diskOffering, vm, template, deviceId,
|
||||
|
|
@ -1644,11 +1644,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
}
|
||||
|
||||
protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster destinationCluster, VMTemplateVO template,
|
||||
String sourceVMName, String displayName, String hostName,
|
||||
Account caller, Account owner, long userId,
|
||||
ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap,
|
||||
Map<String, Long> nicNetworkMap, Map<String, Network.IpAddresses> nicIpAddressMap,
|
||||
Map<String, String> details, ImportVmCmd cmd, boolean forced) throws ResourceAllocationException {
|
||||
String sourceVMName, String displayName, String hostName,
|
||||
Account caller, Account owner, long userId,
|
||||
ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap,
|
||||
Map<String, Long> nicNetworkMap, Map<String, Network.IpAddresses> nicIpAddressMap,
|
||||
Map<String, String> details, ImportVmCmd cmd, boolean forced) throws ResourceAllocationException {
|
||||
Long existingVcenterId = cmd.getExistingVcenterId();
|
||||
String vcenter = cmd.getVcenter();
|
||||
String datacenterName = cmd.getDatacenterName();
|
||||
|
|
@ -1835,7 +1835,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
}
|
||||
if (!volumeApiService.doesStoragePoolSupportDiskOffering(selectedStoragePool, rootDiskOffering)) {
|
||||
throw new InvalidParameterValueException(String.format("The root disk offering '%s' is not supported by the selected conversion storage pool '%s'. " +
|
||||
"When using VDDK, all selected disk offerings must be compatible with the conversion storage pool, as it will become the primary storage for the imported volumes.",
|
||||
"When using VDDK, all selected disk offerings must be compatible with the conversion storage pool, as it will become the primary storage for the imported volumes.",
|
||||
rootDiskOffering.getName(), selectedStoragePool.getName()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1848,7 +1848,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
}
|
||||
if (!volumeApiService.doesStoragePoolSupportDiskOffering(selectedStoragePool, diskOffering)) {
|
||||
throw new InvalidParameterValueException(String.format("The data disk offering '%s' is not supported by the selected conversion storage pool '%s'. " +
|
||||
"When using VDDK, all selected disk offerings must be compatible with the conversion storage pool, as it will become the primary storage for the imported volumes.",
|
||||
"When using VDDK, all selected disk offerings must be compatible with the conversion storage pool, as it will become the primary storage for the imported volumes.",
|
||||
diskOffering.getName(), selectedStoragePool.getName()));
|
||||
}
|
||||
}
|
||||
|
|
@ -2078,9 +2078,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
|
||||
String err = useVddk
|
||||
? String.format("Could not find any suitable %s host in cluster %s with '%s' configured to perform the VDDK-based instance conversion",
|
||||
destinationCluster.getHypervisorType(), destinationCluster, Host.HOST_VDDK_SUPPORT)
|
||||
destinationCluster.getHypervisorType(), destinationCluster, Host.HOST_VDDK_SUPPORT)
|
||||
: String.format("Could not find any suitable %s host in cluster %s to perform the instance conversion",
|
||||
destinationCluster.getHypervisorType(), destinationCluster);
|
||||
destinationCluster.getHypervisorType(), destinationCluster);
|
||||
logger.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
|
|
@ -2708,10 +2708,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
}
|
||||
|
||||
private UserVm importExternalKvmVirtualMachine(final UnmanagedInstanceTO unmanagedInstance, final String instanceName, final DataCenter zone,
|
||||
final VirtualMachineTemplate template, final String displayName, final String hostName, final Account caller, final Account owner, final Long userId,
|
||||
final ServiceOfferingVO serviceOffering, final Map<String, Long> dataDiskOfferingMap,
|
||||
final Map<String, Long> nicNetworkMap, final Map<String, Network.IpAddresses> callerNicIpAddressMap,
|
||||
final String remoteUrl, String username, String password, String tmpPath, final Map<String, String> details) throws ResourceAllocationException {
|
||||
final VirtualMachineTemplate template, final String displayName, final String hostName, final Account caller, final Account owner, final Long userId,
|
||||
final ServiceOfferingVO serviceOffering, final Map<String, Long> dataDiskOfferingMap,
|
||||
final Map<String, Long> nicNetworkMap, final Map<String, Network.IpAddresses> callerNicIpAddressMap,
|
||||
final String remoteUrl, String username, String password, String tmpPath, final Map<String, String> details) throws ResourceAllocationException {
|
||||
UserVm userVm = null;
|
||||
|
||||
Map<String, String> allDetails = new HashMap<>(details);
|
||||
|
|
@ -2744,28 +2744,28 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
}
|
||||
VirtualMachine.PowerState powerState = VirtualMachine.PowerState.PowerOff;
|
||||
|
||||
try {
|
||||
userVm = userVmManager.importVM(zone, null, template, null, displayName, owner,
|
||||
null, caller, true, null, owner.getAccountId(), userId,
|
||||
serviceOffering, null, null, hostName,
|
||||
Hypervisor.HypervisorType.KVM, allDetails, powerState, null);
|
||||
} catch (InsufficientCapacityException ice) {
|
||||
logger.error(String.format("Failed to import vm name: %s", instanceName), ice);
|
||||
throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage());
|
||||
}
|
||||
if (userVm == null) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName));
|
||||
}
|
||||
String rootVolumeName = String.format("ROOT-%s", userVm.getId());
|
||||
DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false);
|
||||
try {
|
||||
userVm = userVmManager.importVM(zone, null, template, null, displayName, owner,
|
||||
null, caller, true, null, owner.getAccountId(), userId,
|
||||
serviceOffering, null, null, hostName,
|
||||
Hypervisor.HypervisorType.KVM, allDetails, powerState, null);
|
||||
} catch (InsufficientCapacityException ice) {
|
||||
logger.error(String.format("Failed to import vm name: %s", instanceName), ice);
|
||||
throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage());
|
||||
}
|
||||
if (userVm == null) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName));
|
||||
}
|
||||
String rootVolumeName = String.format("ROOT-%s", userVm.getId());
|
||||
DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, null, false);
|
||||
|
||||
DiskProfile[] dataDiskProfiles = new DiskProfile[dataDisks.size()];
|
||||
int diskSeq = 0;
|
||||
for (UnmanagedInstanceTO.Disk disk : dataDisks) {
|
||||
DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId()));
|
||||
DiskProfile dataDiskProfile = volumeManager.allocateRawVolume(Volume.Type.DATADISK, String.format("DATA-%d-%s", userVm.getId(), disk.getDiskId()), offering, null, null, null, userVm, template, owner, null, false);
|
||||
dataDiskProfiles[diskSeq++] = dataDiskProfile;
|
||||
}
|
||||
DiskProfile[] dataDiskProfiles = new DiskProfile[dataDisks.size()];
|
||||
int diskSeq = 0;
|
||||
for (UnmanagedInstanceTO.Disk disk : dataDisks) {
|
||||
DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId()));
|
||||
DiskProfile dataDiskProfile = volumeManager.allocateRawVolume(Volume.Type.DATADISK, String.format("DATA-%d-%s", userVm.getId(), disk.getDiskId()), offering, null, null, null, userVm, template, owner, null, null, false);
|
||||
dataDiskProfiles[diskSeq++] = dataDiskProfile;
|
||||
}
|
||||
|
||||
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm, template, serviceOffering, owner, null);
|
||||
ServiceOfferingVO dummyOffering = serviceOfferingDao.findById(userVm.getId(), serviceOffering.getId());
|
||||
|
|
@ -2917,7 +2917,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||
reservations.add(volumeReservation);
|
||||
|
||||
String rootVolumeName = String.format("ROOT-%s", userVm.getId());
|
||||
DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false);
|
||||
DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, null, false);
|
||||
|
||||
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm, template, serviceOffering, owner, null);
|
||||
ServiceOfferingVO dummyOffering = serviceOfferingDao.findById(userVm.getId(), serviceOffering.getId());
|
||||
|
|
|
|||
|
|
@ -330,6 +330,11 @@
|
|||
<property name="caProviders" value="#{caProvidersRegistry.registered}" />
|
||||
</bean>
|
||||
|
||||
<!-- KMS manager -->
|
||||
<bean id="kmsManager" class="org.apache.cloudstack.kms.KMSManagerImpl">
|
||||
<property name="kmsProviders" value="#{kmsProvidersRegistry.registered}" />
|
||||
</bean>
|
||||
|
||||
<bean id="annotationService" class="org.apache.cloudstack.annotation.AnnotationManagerImpl">
|
||||
<property name="kubernetesServiceHelpers" value="#{kubernetesServiceHelperRegistry.registered}" />
|
||||
</bean>
|
||||
|
|
|
|||
|
|
@ -1271,7 +1271,7 @@ public class AutoScaleManagerImplTest {
|
|||
when(zoneMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic);
|
||||
when(userVmService.createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), eq(userData), eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), eq(true), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any())).thenReturn(userVmMock);
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any())).thenReturn(userVmMock);
|
||||
|
||||
UserVm result = autoScaleManagerImplSpy.createNewVM(asVmGroupMock);
|
||||
|
||||
|
|
@ -1282,7 +1282,7 @@ public class AutoScaleManagerImplTest {
|
|||
Mockito.verify(userVmService).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(),
|
||||
matches(vmHostNamePattern), matches(vmHostNamePattern),
|
||||
any(), any(), any(), any(), any(), any(), eq(userData), eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), eq(true), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
Mockito.verify(asVmGroupMock).setNextVmSeq(nextVmSeq + 1);
|
||||
}
|
||||
|
||||
|
|
@ -1318,7 +1318,7 @@ public class AutoScaleManagerImplTest {
|
|||
when(zoneMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced);
|
||||
when(userVmService.createAdvancedSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), eq(userData), eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), eq(true), any(), any(), any(), any())).thenReturn(userVmMock);
|
||||
any(), any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any())).thenReturn(userVmMock);
|
||||
when(networkModel.checkSecurityGroupSupportForNetwork(account, zoneMock,
|
||||
List.of(networkId), Collections.emptyList())).thenReturn(true);
|
||||
|
||||
|
|
@ -1331,7 +1331,7 @@ public class AutoScaleManagerImplTest {
|
|||
Mockito.verify(userVmService).createAdvancedSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(),
|
||||
matches(vmHostNamePattern), matches(vmHostNamePattern),
|
||||
any(), any(), any(), any(), any(), any(), eq(userData), eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any());
|
||||
Mockito.verify(asVmGroupMock).setNextVmSeq(nextVmSeq + 2);
|
||||
}
|
||||
|
||||
|
|
@ -1367,7 +1367,7 @@ public class AutoScaleManagerImplTest {
|
|||
when(zoneMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced);
|
||||
when(userVmService.createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), eq(userData), eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), eq(true), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any())).thenReturn(userVmMock);
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any())).thenReturn(userVmMock);
|
||||
when(networkModel.checkSecurityGroupSupportForNetwork(account, zoneMock,
|
||||
List.of(networkId), Collections.emptyList())).thenReturn(false);
|
||||
|
||||
|
|
@ -1380,7 +1380,7 @@ public class AutoScaleManagerImplTest {
|
|||
Mockito.verify(userVmService).createAdvancedVirtualMachine(any(), any(), any(), any(), any(),
|
||||
matches(vmHostNamePattern), matches(vmHostNamePattern),
|
||||
any(), any(), any(), any(), any(), any(), eq(userData), eq(userDataId), eq(userDataDetails.toString()), any(), any(), any(), eq(true), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any());
|
||||
Mockito.verify(asVmGroupMock).setNextVmSeq(nextVmSeq + 3);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationSe
|
|||
import org.apache.cloudstack.engine.service.api.OrchestrationService;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
||||
import org.apache.cloudstack.kms.KMSManager;
|
||||
import org.apache.cloudstack.network.RoutedIpv4Manager;
|
||||
import org.apache.cloudstack.network.dao.NetworkPermissionDao;
|
||||
import org.apache.cloudstack.region.gslb.GlobalLoadBalancerRuleDao;
|
||||
|
|
@ -212,6 +213,8 @@ public class AccountManagentImplTestBase {
|
|||
AccountService _accountService;
|
||||
@Mock
|
||||
RoutedIpv4Manager routedIpv4Manager;
|
||||
@Mock
|
||||
KMSManager kmsManager;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ public class AccountManagerImplTest extends AccountManagentImplTestBase {
|
|||
Mockito.when(_sshKeyPairDao.remove(Mockito.anyLong())).thenReturn(true);
|
||||
Mockito.when(userDataDao.removeByAccountId(Mockito.anyLong())).thenReturn(222);
|
||||
Mockito.when(sslCertDao.removeByAccountId(Mockito.anyLong())).thenReturn(333);
|
||||
Mockito.when(kmsManager.deleteKMSKeysByAccountId(Mockito.anyLong())).thenReturn(true);
|
||||
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
||||
Mockito.doNothing().when(accountManagerImpl).verifyCallerPrivilegeForUserOrAccountOperations((Account) any());
|
||||
|
||||
|
|
|
|||
|
|
@ -1213,14 +1213,14 @@ public class UserVmManagerImplTest {
|
|||
when(_dcMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic);
|
||||
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
|
||||
UserVm result = userVmManagerImpl.createVirtualMachine(deployVMCmd);
|
||||
assertEquals(userVmVoMock, result);
|
||||
Mockito.verify(vnfTemplateManager).validateVnfApplianceNics(templateMock, null, Collections.emptyMap());
|
||||
Mockito.verify(userVmManagerImpl).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
private List<VolumeVO> mockVolumesForIsAnyVmVolumeUsingLocalStorageTest(int localVolumes, int nonLocalVolumes) {
|
||||
|
|
@ -1473,7 +1473,7 @@ public class UserVmManagerImplTest {
|
|||
|
||||
doThrow(cre).when(userVmManagerImpl).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
|
||||
CloudRuntimeException creThrown = assertThrows(CloudRuntimeException.class, () -> userVmManagerImpl.createVirtualMachine(deployVMCmd));
|
||||
ArrayList<ExceptionProxyObject> proxyIdList = creThrown.getIdProxyList();
|
||||
|
|
@ -3375,7 +3375,7 @@ public class UserVmManagerImplTest {
|
|||
|
||||
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any());
|
||||
|
||||
UserVm result = userVmManagerImpl.allocateVMFromBackup(cmd);
|
||||
|
||||
|
|
@ -3383,7 +3383,7 @@ public class UserVmManagerImplTest {
|
|||
Mockito.verify(backupDao).findById(backupId);
|
||||
Mockito.verify(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -3434,14 +3434,14 @@ public class UserVmManagerImplTest {
|
|||
|
||||
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(false), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(false), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(false), any(), any(), any(), any(), any());
|
||||
|
||||
UserVm result = userVmManagerImpl.allocateVMFromBackup(cmd);
|
||||
|
||||
assertNotNull(result);
|
||||
Mockito.verify(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(false), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(false), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(false), any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -3551,7 +3551,7 @@ public class UserVmManagerImplTest {
|
|||
|
||||
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any());
|
||||
|
||||
UserVm result = userVmManagerImpl.allocateVMFromBackup(cmd);
|
||||
|
||||
|
|
@ -3559,7 +3559,7 @@ public class UserVmManagerImplTest {
|
|||
Mockito.verify(backupDao).findById(backupId);
|
||||
Mockito.verify(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -3613,14 +3613,14 @@ public class UserVmManagerImplTest {
|
|||
|
||||
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(false), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(false), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(false), any(), any(), any(), any(), any());
|
||||
|
||||
UserVm result = userVmManagerImpl.allocateVMFromBackup(cmd);
|
||||
|
||||
assertNotNull(result);
|
||||
Mockito.verify(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), eq(false), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(false), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(false), any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -3918,7 +3918,7 @@ public class UserVmManagerImplTest {
|
|||
when(_dcMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic);
|
||||
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
|
||||
|
||||
userVmManagerImpl.createVirtualMachine(deployVMCmd);
|
||||
|
|
@ -3956,7 +3956,7 @@ public class UserVmManagerImplTest {
|
|||
when(_dcMock.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic);
|
||||
Mockito.doReturn(userVmVoMock).when(userVmManagerImpl).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
|
||||
|
||||
userVmManagerImpl.createVirtualMachine(deployVMCmd);
|
||||
|
|
@ -4007,7 +4007,7 @@ public class UserVmManagerImplTest {
|
|||
when(createdVm.getId()).thenReturn(2L);
|
||||
Mockito.doReturn(createdVm).when(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any());
|
||||
|
||||
Map<String, String> existingDetails = new HashMap<>();
|
||||
existingDetails.put("existingKey", "existingValue");
|
||||
|
|
@ -4075,7 +4075,7 @@ public class UserVmManagerImplTest {
|
|||
when(createdVm.getId()).thenReturn(2L);
|
||||
Mockito.doReturn(createdVm).when(userVmManagerImpl).createAdvancedVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
|
||||
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any());
|
||||
any(), any(), any(), any(), eq(true), any(), any(), any(), any(), any());
|
||||
|
||||
UserVm result = userVmManagerImpl.allocateVMFromBackup(cmd);
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue