mirror of https://github.com/apache/cloudstack.git
Merge branch 'main' of https://github.com/apache/cloudstack into clone-edit-existing-offerings
This commit is contained in:
commit
6e68f80106
|
|
@ -26,3 +26,5 @@ updates:
|
|||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
|
|
|||
|
|
@ -33,9 +33,11 @@ jobs:
|
|||
stale-issue-message: 'This issue is stale because it has been open for 120 days with no activity. It may be removed by administrators of this project at any time. Remove the stale label or comment to request for removal of it to prevent this.'
|
||||
stale-pr-message: 'This PR is stale because it has been open for 120 days with no activity. It may be removed by administrators of this project at any time. Remove the stale label or comment to request for removal of it to prevent this.'
|
||||
close-issue-message: 'This issue was closed because it has been stale for 120 days with no activity.'
|
||||
close-pr-message: 'This PR was closed because it has been stale for 120 days with no activity.'
|
||||
close-pr-message: 'This PR was closed because it has been stale for 240 days with no activity.'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
||||
days-before-stale: 120
|
||||
days-before-close: -1
|
||||
days-before-pr-close: 240
|
||||
exempt-issue-labels: 'gsoc,good-first-issue,long-term-plan'
|
||||
exempt-pr-labels: 'status:ready-for-merge,status:needs-testing,status:on-hold'
|
||||
|
|
|
|||
|
|
@ -62,6 +62,16 @@ repos:
|
|||
- .github/workflows/license-templates/LICENSE.txt
|
||||
- --fuzzy-match-generates-todo
|
||||
exclude: ^(CHANGES|ISSUE_TEMPLATE|PULL_REQUEST_TEMPLATE)\.md$|^ui/docs/(full|smoke)-test-plan\.template\.md$
|
||||
- id: insert-license
|
||||
name: add license for all properties files
|
||||
description: automatically adds a licence header to all properties files that don't have a license header
|
||||
files: \.properties$
|
||||
args:
|
||||
- --comment-style
|
||||
- '|#|'
|
||||
- --license-filepath
|
||||
- .github/workflows/license-templates/LICENSE.txt
|
||||
- --fuzzy-match-generates-todo
|
||||
- id: insert-license
|
||||
name: add license for all Shell files
|
||||
description: automatically adds a licence header to all Shell files that don't have a license header
|
||||
|
|
|
|||
|
|
@ -504,6 +504,7 @@ public class EventTypes {
|
|||
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE = "VPN.S2S.CUSTOMER.GATEWAY.CREATE";
|
||||
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE = "VPN.S2S.CUSTOMER.GATEWAY.DELETE";
|
||||
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE = "VPN.S2S.CUSTOMER.GATEWAY.UPDATE";
|
||||
public static final String EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS = "VPN.S2S.GATEWAY.OBSOLETE.PARAMS";
|
||||
public static final String EVENT_S2S_VPN_CONNECTION_CREATE = "VPN.S2S.CONNECTION.CREATE";
|
||||
public static final String EVENT_S2S_VPN_CONNECTION_DELETE = "VPN.S2S.CONNECTION.DELETE";
|
||||
public static final String EVENT_S2S_VPN_CONNECTION_RESET = "VPN.S2S.CONNECTION.RESET";
|
||||
|
|
@ -1153,6 +1154,7 @@ public class EventTypes {
|
|||
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE, Site2SiteCustomerGateway.class);
|
||||
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE, Site2SiteCustomerGateway.class);
|
||||
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE, Site2SiteCustomerGateway.class);
|
||||
entityEventDetails.put(EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS, Site2SiteCustomerGateway.class);
|
||||
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_CREATE, Site2SiteVpnConnection.class);
|
||||
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_DELETE, Site2SiteVpnConnection.class);
|
||||
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_RESET, Site2SiteVpnConnection.class);
|
||||
|
|
|
|||
|
|
@ -125,6 +125,10 @@ public interface NetworkModel {
|
|||
*/
|
||||
String getNextAvailableMacAddressInNetwork(long networkConfigurationId) throws InsufficientAddressCapacityException;
|
||||
|
||||
String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException;
|
||||
|
||||
boolean isMACUnique(String mac, long networkId);
|
||||
|
||||
PublicIpAddress getPublicIpAddress(long ipAddressId);
|
||||
|
||||
List<? extends Vlan> listPodVlans(long podId);
|
||||
|
|
@ -364,4 +368,8 @@ public interface NetworkModel {
|
|||
|
||||
boolean checkSecurityGroupSupportForNetwork(Account account, DataCenter zone, List<Long> networkIds,
|
||||
List<Long> securityGroupsIds);
|
||||
|
||||
default long getMacIdentifier(Long dataCenterId) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import com.cloud.offering.DiskOffering;
|
|||
import com.cloud.offering.NetworkOffering;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
|
||||
public interface AccountService {
|
||||
|
||||
|
|
@ -115,6 +116,8 @@ public interface AccountService {
|
|||
|
||||
void checkAccess(Account account, VpcOffering vof, DataCenter zone) throws PermissionDeniedException;
|
||||
|
||||
void checkAccess(Account account, BackupOffering bof) throws PermissionDeniedException;
|
||||
|
||||
void checkAccess(User user, ControlledEntity entity);
|
||||
|
||||
void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, ControlledEntity... entities) throws PermissionDeniedException;
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ public interface VmDetailConstants {
|
|||
String NIC_MULTIQUEUE_NUMBER = "nic.multiqueue.number";
|
||||
String NIC_PACKED_VIRTQUEUES_ENABLED = "nic.packed.virtqueues.enabled";
|
||||
|
||||
// KVM specific, disk controllers
|
||||
String KVM_SKIP_FORCE_DISK_CONTROLLER = "skip.force.disk.controller";
|
||||
|
||||
// Mac OSX guest specific (internal)
|
||||
String SMC_PRESENT = "smc.present";
|
||||
String FIRMWARE = "firmware";
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ import com.cloud.user.Account;
|
|||
import com.cloud.user.User;
|
||||
import com.cloud.utils.component.Adapter;
|
||||
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
|
||||
/**
|
||||
* SecurityChecker checks the ownership and access control to objects within
|
||||
*/
|
||||
|
|
@ -145,4 +147,6 @@ public interface SecurityChecker extends Adapter {
|
|||
boolean checkAccess(Account account, NetworkOffering nof, DataCenter zone) throws PermissionDeniedException;
|
||||
|
||||
boolean checkAccess(Account account, VpcOffering vof, DataCenter zone) throws PermissionDeniedException;
|
||||
|
||||
boolean checkAccess(Account account, BackupOffering bof) throws PermissionDeniedException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ public interface AlertService {
|
|||
public static final AlertType ALERT_TYPE_VR_PUBLIC_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PUBLIC.IFACE.MTU", true);
|
||||
public static final AlertType ALERT_TYPE_VR_PRIVATE_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PRIVATE.IFACE.MTU", true);
|
||||
public static final AlertType ALERT_TYPE_EXTENSION_PATH_NOT_READY = new AlertType((short)33, "ALERT.TYPE.EXTENSION.PATH.NOT.READY", true);
|
||||
public static final AlertType ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS = new AlertType((short)34, "ALERT.S2S.VPN.GATEWAY.OBSOLETE.PARAMETERS", true);
|
||||
public static final AlertType ALERT_TYPE_BACKUP_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_BACKUP_STORAGE, "ALERT.STORAGE.BACKUP", true);
|
||||
public static final AlertType ALERT_TYPE_OBJECT_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_OBJECT_STORAGE, "ALERT.STORAGE.OBJECT", true);
|
||||
|
||||
|
|
|
|||
|
|
@ -375,6 +375,7 @@ public class ApiConstants {
|
|||
public static final String MAC_ADDRESS = "macaddress";
|
||||
public static final String MAC_ADDRESSES = "macaddresses";
|
||||
public static final String MANUAL_UPGRADE = "manualupgrade";
|
||||
public static final String MATCH_TYPE = "matchtype";
|
||||
public static final String MAX = "max";
|
||||
public static final String MAX_SNAPS = "maxsnaps";
|
||||
public static final String MAX_BACKUPS = "maxbackups";
|
||||
|
|
@ -1364,6 +1365,10 @@ public class ApiConstants {
|
|||
|
||||
public static final String RECURSIVE_DOMAINS = "recursivedomains";
|
||||
|
||||
public static final String VPN_CUSTOMER_GATEWAY_PARAMETERS = "vpncustomergatewayparameters";
|
||||
public static final String OBSOLETE_PARAMETERS = "obsoleteparameters";
|
||||
public static final String EXCLUDED_PARAMETERS = "excludedparameters";
|
||||
|
||||
/**
|
||||
* This enum specifies IO Drivers, each option controls specific policies on I/O.
|
||||
* Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import org.apache.cloudstack.api.response.ListResponse;
|
|||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
public abstract class BaseBackupListCmd extends BaseListCmd {
|
||||
public abstract class BaseBackupListCmd extends BaseListAccountResourcesCmd {
|
||||
|
||||
protected void setupResponseBackupOfferingsList(final List<BackupOffering> offerings, final Integer count) {
|
||||
final ListResponse<BackupOfferingResponse> response = new ListResponse<>();
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import org.apache.cloudstack.api.BaseCmd;
|
|||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.BackupOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.backup.BackupManager;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
|
|
@ -40,6 +41,11 @@ import com.cloud.exception.NetworkRuleConflictException;
|
|||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@APICommand(name = "importBackupOffering",
|
||||
description = "Imports a backup offering using a backup provider",
|
||||
|
|
@ -76,6 +82,13 @@ public class ImportBackupOfferingCmd extends BaseAsyncCmd {
|
|||
description = "Whether users are allowed to create adhoc backups and backup schedules", required = true)
|
||||
private Boolean userDrivenBackups;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID,
|
||||
type = CommandType.LIST,
|
||||
collectionType = CommandType.UUID,
|
||||
entityType = DomainResponse.class,
|
||||
description = "the ID of the containing domain(s), null for public offerings")
|
||||
private List<Long> domainIds;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -100,6 +113,15 @@ public class ImportBackupOfferingCmd extends BaseAsyncCmd {
|
|||
return userDrivenBackups == null ? false : userDrivenBackups;
|
||||
}
|
||||
|
||||
public List<Long> getDomainIds() {
|
||||
if (CollectionUtils.isNotEmpty(domainIds)) {
|
||||
Set<Long> set = new LinkedHashSet<>(domainIds);
|
||||
domainIds.clear();
|
||||
domainIds.addAll(set);
|
||||
}
|
||||
return domainIds;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -25,19 +25,24 @@ 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.offering.DomainAndZoneIdResolver;
|
||||
import org.apache.cloudstack.api.response.BackupOfferingResponse;
|
||||
import org.apache.cloudstack.backup.BackupManager;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.LongFunction;
|
||||
|
||||
@APICommand(name = "updateBackupOffering", description = "Updates a backup offering.", responseObject = BackupOfferingResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.16.0")
|
||||
public class UpdateBackupOfferingCmd extends BaseCmd {
|
||||
public class UpdateBackupOfferingCmd extends BaseCmd implements DomainAndZoneIdResolver {
|
||||
|
||||
@Inject
|
||||
private BackupManager backupManager;
|
||||
|
|
@ -57,6 +62,13 @@ public class UpdateBackupOfferingCmd extends BaseCmd {
|
|||
@Parameter(name = ApiConstants.ALLOW_USER_DRIVEN_BACKUPS, type = CommandType.BOOLEAN, description = "Whether to allow user driven backups or not")
|
||||
private Boolean allowUserDrivenBackups;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID,
|
||||
type = CommandType.STRING,
|
||||
description = "the ID of the containing domain(s) as comma separated string, public for public offerings",
|
||||
since = "4.23.0",
|
||||
length = 4096)
|
||||
private String domainIds;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -82,7 +94,7 @@ public class UpdateBackupOfferingCmd extends BaseCmd {
|
|||
@Override
|
||||
public void execute() {
|
||||
try {
|
||||
if (StringUtils.isAllEmpty(getName(), getDescription()) && getAllowUserDrivenBackups() == null) {
|
||||
if (StringUtils.isAllEmpty(getName(), getDescription()) && getAllowUserDrivenBackups() == null && CollectionUtils.isEmpty(getDomainIds())) {
|
||||
throw new InvalidParameterValueException(String.format("Can't update Backup Offering [id: %s] because there are no parameters to be updated, at least one of the",
|
||||
"following should be informed: name, description or allowUserDrivenBackups.", id));
|
||||
}
|
||||
|
|
@ -103,6 +115,18 @@ public class UpdateBackupOfferingCmd extends BaseCmd {
|
|||
}
|
||||
}
|
||||
|
||||
public List<Long> getDomainIds() {
|
||||
// backupManager may be null in unit tests where the command is spied without injection.
|
||||
// Avoid creating a method reference to a null receiver which causes NPE. When backupManager
|
||||
// is null, pass null as the defaultDomainsProvider so resolveDomainIds will simply return
|
||||
// an empty list or parse the explicit domainIds string.
|
||||
LongFunction<List<Long>> defaultDomainsProvider = null;
|
||||
if (backupManager != null) {
|
||||
defaultDomainsProvider = backupManager::getBackupOfferingDomains;
|
||||
}
|
||||
return resolveDomainIds(domainIds, id, defaultDomainsProvider, "backup offering");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.api.command.admin.network;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
|
|
@ -26,18 +25,16 @@ 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.offering.DomainAndZoneIdResolver;
|
||||
import org.apache.cloudstack.api.response.NetworkOfferingResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
|
||||
import com.cloud.offering.NetworkOffering;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@APICommand(name = "updateNetworkOffering", description = "Updates a network offering.", responseObject = NetworkOfferingResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
public class UpdateNetworkOfferingCmd extends BaseCmd {
|
||||
public class UpdateNetworkOfferingCmd extends BaseCmd implements DomainAndZoneIdResolver {
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
|
|
@ -129,63 +126,11 @@ public class UpdateNetworkOfferingCmd extends BaseCmd {
|
|||
}
|
||||
|
||||
public List<Long> getDomainIds() {
|
||||
List<Long> validDomainIds = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(domainIds)) {
|
||||
if (domainIds.contains(",")) {
|
||||
String[] domains = domainIds.split(",");
|
||||
for (String domain : domains) {
|
||||
Domain validDomain = _entityMgr.findByUuid(Domain.class, domain.trim());
|
||||
if (validDomain != null) {
|
||||
validDomainIds.add(validDomain.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create network offering because invalid domain has been specified.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
domainIds = domainIds.trim();
|
||||
if (!domainIds.matches("public")) {
|
||||
Domain validDomain = _entityMgr.findByUuid(Domain.class, domainIds.trim());
|
||||
if (validDomain != null) {
|
||||
validDomainIds.add(validDomain.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create network offering because invalid domain has been specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validDomainIds.addAll(_configService.getNetworkOfferingDomains(id));
|
||||
}
|
||||
return validDomainIds;
|
||||
return resolveDomainIds(domainIds, id, _configService::getNetworkOfferingDomains, "network offering");
|
||||
}
|
||||
|
||||
public List<Long> getZoneIds() {
|
||||
List<Long> validZoneIds = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(zoneIds)) {
|
||||
if (zoneIds.contains(",")) {
|
||||
String[] zones = zoneIds.split(",");
|
||||
for (String zone : zones) {
|
||||
DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zone.trim());
|
||||
if (validZone != null) {
|
||||
validZoneIds.add(validZone.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create network offering because invalid zone has been specified.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zoneIds = zoneIds.trim();
|
||||
if (!zoneIds.matches("all")) {
|
||||
DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zoneIds.trim());
|
||||
if (validZone != null) {
|
||||
validZoneIds.add(validZone.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create network offering because invalid zone has been specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validZoneIds.addAll(_configService.getNetworkOfferingZones(id));
|
||||
}
|
||||
return validZoneIds;
|
||||
return resolveZoneIds(zoneIds, id, _configService::getNetworkOfferingZones, "network offering");
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.api.command.admin.offering;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.offering.DiskOffering.State;
|
||||
|
|
@ -27,19 +26,18 @@ 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.offering.DomainAndZoneIdResolver;
|
||||
import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.offering.DiskOffering;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@APICommand(name = "updateDiskOffering", description = "Updates a disk offering.", responseObject = DiskOfferingResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
public class UpdateDiskOfferingCmd extends BaseCmd {
|
||||
public class UpdateDiskOfferingCmd extends BaseCmd implements DomainAndZoneIdResolver {
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
|
|
@ -151,63 +149,11 @@ public class UpdateDiskOfferingCmd extends BaseCmd {
|
|||
}
|
||||
|
||||
public List<Long> getDomainIds() {
|
||||
List<Long> validDomainIds = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(domainIds)) {
|
||||
if (domainIds.contains(",")) {
|
||||
String[] domains = domainIds.split(",");
|
||||
for (String domain : domains) {
|
||||
Domain validDomain = _entityMgr.findByUuid(Domain.class, domain.trim());
|
||||
if (validDomain != null) {
|
||||
validDomainIds.add(validDomain.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create disk offering because invalid domain has been specified.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
domainIds = domainIds.trim();
|
||||
if (!domainIds.matches("public")) {
|
||||
Domain validDomain = _entityMgr.findByUuid(Domain.class, domainIds.trim());
|
||||
if (validDomain != null) {
|
||||
validDomainIds.add(validDomain.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create disk offering because invalid domain has been specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validDomainIds.addAll(_configService.getDiskOfferingDomains(id));
|
||||
}
|
||||
return validDomainIds;
|
||||
return resolveDomainIds(domainIds, id, _configService::getDiskOfferingDomains, "disk offering");
|
||||
}
|
||||
|
||||
public List<Long> getZoneIds() {
|
||||
List<Long> validZoneIds = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(zoneIds)) {
|
||||
if (zoneIds.contains(",")) {
|
||||
String[] zones = zoneIds.split(",");
|
||||
for (String zone : zones) {
|
||||
DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zone.trim());
|
||||
if (validZone != null) {
|
||||
validZoneIds.add(validZone.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create disk offering because invalid zone has been specified.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zoneIds = zoneIds.trim();
|
||||
if (!zoneIds.matches("all")) {
|
||||
DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zoneIds.trim());
|
||||
if (validZone != null) {
|
||||
validZoneIds.add(validZone.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create disk offering because invalid zone has been specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validZoneIds.addAll(_configService.getDiskOfferingZones(id));
|
||||
}
|
||||
return validZoneIds;
|
||||
return resolveZoneIds(zoneIds, id, _configService::getDiskOfferingZones, "disk offering");
|
||||
}
|
||||
|
||||
public String getTags() {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.api.command.admin.offering;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -28,19 +27,18 @@ 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.offering.DomainAndZoneIdResolver;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@APICommand(name = "updateServiceOffering", description = "Updates a service offering.", responseObject = ServiceOfferingResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
public class UpdateServiceOfferingCmd extends BaseCmd {
|
||||
public class UpdateServiceOfferingCmd extends BaseCmd implements DomainAndZoneIdResolver {
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
|
|
@ -130,63 +128,11 @@ public class UpdateServiceOfferingCmd extends BaseCmd {
|
|||
}
|
||||
|
||||
public List<Long> getDomainIds() {
|
||||
List<Long> validDomainIds = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(domainIds)) {
|
||||
if (domainIds.contains(",")) {
|
||||
String[] domains = domainIds.split(",");
|
||||
for (String domain : domains) {
|
||||
Domain validDomain = _entityMgr.findByUuid(Domain.class, domain.trim());
|
||||
if (validDomain != null) {
|
||||
validDomainIds.add(validDomain.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create service offering because invalid domain has been specified.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
domainIds = domainIds.trim();
|
||||
if (!domainIds.matches("public")) {
|
||||
Domain validDomain = _entityMgr.findByUuid(Domain.class, domainIds.trim());
|
||||
if (validDomain != null) {
|
||||
validDomainIds.add(validDomain.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create service offering because invalid domain has been specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validDomainIds.addAll(_configService.getServiceOfferingDomains(id));
|
||||
}
|
||||
return validDomainIds;
|
||||
return resolveDomainIds(domainIds, id, _configService::getServiceOfferingDomains, "service offering");
|
||||
}
|
||||
|
||||
public List<Long> getZoneIds() {
|
||||
List<Long> validZoneIds = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(zoneIds)) {
|
||||
if (zoneIds.contains(",")) {
|
||||
String[] zones = zoneIds.split(",");
|
||||
for (String zone : zones) {
|
||||
DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zone.trim());
|
||||
if (validZone != null) {
|
||||
validZoneIds.add(validZone.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create service offering because invalid zone has been specified.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zoneIds = zoneIds.trim();
|
||||
if (!zoneIds.matches("all")) {
|
||||
DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zoneIds.trim());
|
||||
if (validZone != null) {
|
||||
validZoneIds.add(validZone.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create service offering because invalid zone has been specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validZoneIds.addAll(_configService.getServiceOfferingZones(id));
|
||||
}
|
||||
return validZoneIds;
|
||||
return resolveZoneIds(zoneIds, id, _configService::getServiceOfferingZones, "service offering");
|
||||
}
|
||||
|
||||
public String getStorageTags() {
|
||||
|
|
|
|||
|
|
@ -78,12 +78,12 @@ public class DisableUserCmd extends BaseAsyncCmd {
|
|||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "disabling user: " + getId();
|
||||
return "disabling user: " + this._uuidMgr.getUuid(User.class, getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
CallContext.current().setEventDetails("UserId: " + getId());
|
||||
CallContext.current().setEventDetails("User ID: " + this._uuidMgr.getUuid(User.class, getId()));
|
||||
UserAccount user = _regionService.disableUser(this);
|
||||
|
||||
if (user != null) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.api.command.admin.vpc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
|
|
@ -26,19 +25,16 @@ 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.offering.DomainAndZoneIdResolver;
|
||||
import org.apache.cloudstack.api.response.VpcOfferingResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.network.vpc.VpcOffering;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@APICommand(name = "updateVPCOffering", description = "Updates VPC offering", responseObject = VpcOfferingResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
public class UpdateVPCOfferingCmd extends BaseAsyncCmd {
|
||||
public class UpdateVPCOfferingCmd extends BaseAsyncCmd implements DomainAndZoneIdResolver {
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
|
|
@ -92,63 +88,11 @@ public class UpdateVPCOfferingCmd extends BaseAsyncCmd {
|
|||
}
|
||||
|
||||
public List<Long> getDomainIds() {
|
||||
List<Long> validDomainIds = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(domainIds)) {
|
||||
if (domainIds.contains(",")) {
|
||||
String[] domains = domainIds.split(",");
|
||||
for (String domain : domains) {
|
||||
Domain validDomain = _entityMgr.findByUuid(Domain.class, domain.trim());
|
||||
if (validDomain != null) {
|
||||
validDomainIds.add(validDomain.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create VPC offering because invalid domain has been specified.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
domainIds = domainIds.trim();
|
||||
if (!domainIds.matches("public")) {
|
||||
Domain validDomain = _entityMgr.findByUuid(Domain.class, domainIds.trim());
|
||||
if (validDomain != null) {
|
||||
validDomainIds.add(validDomain.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create VPC offering because invalid domain has been specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validDomainIds.addAll(_vpcProvSvc.getVpcOfferingDomains(id));
|
||||
}
|
||||
return validDomainIds;
|
||||
return resolveDomainIds(domainIds, id, _vpcProvSvc::getVpcOfferingDomains, "VPC offering");
|
||||
}
|
||||
|
||||
public List<Long> getZoneIds() {
|
||||
List<Long> validZoneIds = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(zoneIds)) {
|
||||
if (zoneIds.contains(",")) {
|
||||
String[] zones = zoneIds.split(",");
|
||||
for (String zone : zones) {
|
||||
DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zone.trim());
|
||||
if (validZone != null) {
|
||||
validZoneIds.add(validZone.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create VPC offering because invalid zone has been specified.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zoneIds = zoneIds.trim();
|
||||
if (!zoneIds.matches("all")) {
|
||||
DataCenter validZone = _entityMgr.findByUuid(DataCenter.class, zoneIds.trim());
|
||||
if (validZone != null) {
|
||||
validZoneIds.add(validZone.getId());
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Failed to create VPC offering because invalid zone has been specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validZoneIds.addAll(_vpcProvSvc.getVpcOfferingZones(id));
|
||||
}
|
||||
return validZoneIds;
|
||||
return resolveZoneIds(zoneIds, id, _vpcProvSvc::getVpcOfferingZones, "VPC offering");
|
||||
}
|
||||
|
||||
public Integer getSortKey() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
// 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.offering;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.LongFunction;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Helper for commands that accept a domainIds or zoneIds string and need to
|
||||
* resolve them to lists of IDs, falling back to an offering-specific
|
||||
* default provider.
|
||||
*/
|
||||
public interface DomainAndZoneIdResolver {
|
||||
/**
|
||||
* Parse the provided domainIds string and return a list of domain IDs.
|
||||
* If domainIds is empty, the defaultDomainsProvider will be invoked with the
|
||||
* provided resource id to obtain the current domains.
|
||||
*/
|
||||
default List<Long> resolveDomainIds(final String domainIds, final Long id, final LongFunction<List<Long>> defaultDomainsProvider, final String resourceTypeName) {
|
||||
final List<Long> validDomainIds = new ArrayList<>();
|
||||
final BaseCmd base = (BaseCmd) this;
|
||||
final Logger logger = LogManager.getLogger(base.getClass());
|
||||
|
||||
if (StringUtils.isEmpty(domainIds)) {
|
||||
if (defaultDomainsProvider != null) {
|
||||
final List<Long> defaults = defaultDomainsProvider.apply(id);
|
||||
if (defaults != null) {
|
||||
validDomainIds.addAll(defaults);
|
||||
}
|
||||
}
|
||||
return validDomainIds;
|
||||
}
|
||||
|
||||
final String[] domains = domainIds.split(",");
|
||||
final String type = (resourceTypeName == null || resourceTypeName.isEmpty()) ? "offering" : resourceTypeName;
|
||||
for (String domain : domains) {
|
||||
final String trimmed = domain == null ? "" : domain.trim();
|
||||
if (trimmed.isEmpty() || "public".equalsIgnoreCase(trimmed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Domain validDomain = base._entityMgr.findByUuid(Domain.class, trimmed);
|
||||
if (validDomain == null) {
|
||||
logger.warn("Invalid domain specified for {}", type);
|
||||
throw new InvalidParameterValueException("Failed to create " + type + " because invalid domain has been specified.");
|
||||
}
|
||||
validDomainIds.add(validDomain.getId());
|
||||
}
|
||||
|
||||
return validDomainIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the provided zoneIds string and return a list of zone IDs.
|
||||
* If zoneIds is empty, the defaultZonesProvider will be invoked with the
|
||||
* provided resource id to obtain the current zones.
|
||||
*/
|
||||
default List<Long> resolveZoneIds(final String zoneIds, final Long id, final LongFunction<List<Long>> defaultZonesProvider, final String resourceTypeName) {
|
||||
final List<Long> validZoneIds = new ArrayList<>();
|
||||
final BaseCmd base = (BaseCmd) this;
|
||||
final Logger logger = LogManager.getLogger(base.getClass());
|
||||
|
||||
if (StringUtils.isEmpty(zoneIds)) {
|
||||
if (defaultZonesProvider != null) {
|
||||
final List<Long> defaults = defaultZonesProvider.apply(id);
|
||||
if (defaults != null) {
|
||||
validZoneIds.addAll(defaults);
|
||||
}
|
||||
}
|
||||
return validZoneIds;
|
||||
}
|
||||
|
||||
final String[] zones = zoneIds.split(",");
|
||||
final String type = (resourceTypeName == null || resourceTypeName.isEmpty()) ? "offering" : resourceTypeName;
|
||||
for (String zone : zones) {
|
||||
final String trimmed = zone == null ? "" : zone.trim();
|
||||
if (trimmed.isEmpty() || "all".equalsIgnoreCase(trimmed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final DataCenter validZone = base._entityMgr.findByUuid(DataCenter.class, trimmed);
|
||||
if (validZone == null) {
|
||||
logger.warn("Invalid zone specified for {}: {}", type, trimmed);
|
||||
throw new InvalidParameterValueException("Failed to create " + type + " because invalid zone has been specified.");
|
||||
}
|
||||
validZoneIds.add(validZone.getId());
|
||||
}
|
||||
|
||||
return validZoneIds;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ package org.apache.cloudstack.api.command.user.backup;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
|
|
@ -138,7 +139,8 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd {
|
|||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Creating backup for Instance " + vmId;
|
||||
String vmUuid = _uuidMgr.getUuid(VirtualMachine.class, getVmId());
|
||||
return "Creating backup for Instance " + vmUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ 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.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.BackupScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.backup.BackupManager;
|
||||
|
|
@ -38,7 +37,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
|||
|
||||
@APICommand(name = "createBackupSchedule",
|
||||
description = "Creates a User-defined Instance backup schedule",
|
||||
responseObject = BackupResponse.class, since = "4.14.0",
|
||||
responseObject = BackupScheduleResponse.class, since = "4.14.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class CreateBackupScheduleCmd extends BaseCmd {
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import org.apache.cloudstack.api.Parameter;
|
|||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.backup.BackupManager;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
|
@ -111,6 +112,7 @@ public class DeleteBackupCmd extends BaseAsyncCmd {
|
|||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Deleting backup ID " + backupId;
|
||||
String backupUuid = _uuidMgr.getUuid(Backup.class, getId());
|
||||
return "Deleting backup ID " + backupUuid;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import org.apache.cloudstack.api.Parameter;
|
|||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.backup.BackupManager;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
|
|
@ -99,6 +100,7 @@ public class RestoreBackupCmd extends BaseAsyncCmd {
|
|||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
return "Restoring Instance from backup: " + backupId;
|
||||
String backupUuid = _uuidMgr.getUuid(Backup.class, getBackupId());
|
||||
return "Restoring Instance from backup: " + backupUuid;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.apache.cloudstack.api.command.user.backup.repository;
|
||||
|
||||
import com.cloud.utils.StringUtils;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
|
|
@ -100,7 +101,7 @@ public class AddBackupRepositoryCmd extends BaseCmd {
|
|||
}
|
||||
|
||||
public String getMountOptions() {
|
||||
return mountOptions == null ? "" : mountOptions;
|
||||
return StringUtils.isBlank(mountOptions) ? "" : mountOptions;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ import java.util.Map;
|
|||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.CapabilitiesResponse;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.config.ApiServiceConfiguration;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
|
|
@ -30,12 +32,22 @@ import com.cloud.user.Account;
|
|||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
public class ListCapabilitiesCmd extends BaseCmd {
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = DomainResponse.class,
|
||||
description = "the domain for listing capabilities.",
|
||||
since = "4.23.0")
|
||||
private Long domainId;
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
public Long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
Map<String, Object> capabilities = _mgr.listCapabilities(this);
|
||||
|
|
@ -76,6 +88,10 @@ public class ListCapabilitiesCmd extends BaseCmd {
|
|||
response.setExtensionsPath((String)capabilities.get(ApiConstants.EXTENSIONS_PATH));
|
||||
response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED));
|
||||
response.setAdditionalConfigEnabled((Boolean) capabilities.get(ApiConstants.ADDITONAL_CONFIG_ENABLED));
|
||||
if (capabilities.containsKey(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS)) {
|
||||
Map<String, Object> vpnCustomerGatewayParameters = (Map<String, Object>) capabilities.get(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS);
|
||||
response.setVpnCustomerGatewayParameters(vpnCustomerGatewayParameters);
|
||||
}
|
||||
response.setObjectName("capability");
|
||||
response.setResponseName(getCommandName());
|
||||
this.setResponseObject(response);
|
||||
|
|
|
|||
|
|
@ -97,7 +97,11 @@ public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd {
|
|||
"The snapshot will always be made available in the zone in which the volume is present. Currently supported for StorPool only")
|
||||
protected List<Long> storagePoolIds;
|
||||
|
||||
@Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, type=CommandType.BOOLEAN, required = false, since = "4.21.0", description = "This parameter enables the option the snapshot to be copied to supported primary storage")
|
||||
@Parameter (name = ApiConstants.USE_STORAGE_REPLICATION,
|
||||
type=CommandType.BOOLEAN,
|
||||
since = "4.21.0",
|
||||
description = "Enables the snapshot to be copied to the supported primary storages when the config 'use.storage.replication' is set to true for the storage or globally. " +
|
||||
"This is supported only for StorPool storage for now.")
|
||||
protected Boolean useStorageReplication;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -112,7 +112,10 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
|
|||
since = "4.21.0")
|
||||
protected List<Long> storagePoolIds;
|
||||
|
||||
@Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, type=CommandType.BOOLEAN, required = false, description = "This parameter enables the option the snapshot to be copied to supported primary storage")
|
||||
@Parameter (name = ApiConstants.USE_STORAGE_REPLICATION,
|
||||
type=CommandType.BOOLEAN,
|
||||
description = "Enables the snapshot to be copied to the supported primary storages when the config 'use.storage.replication' is set to true for the storage or globally. " +
|
||||
"This is supported only for StorPool storage for now.")
|
||||
protected Boolean useStorageReplication;
|
||||
|
||||
private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject;
|
||||
|
|
|
|||
|
|
@ -94,7 +94,11 @@ public class CreateSnapshotPolicyCmd extends BaseCmd {
|
|||
since = "4.21.0")
|
||||
protected List<Long> storagePoolIds;
|
||||
|
||||
@Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, type=CommandType.BOOLEAN, required = false, since = "4.21.0", description = "This parameter enables the option the snapshot to be copied to supported primary storage")
|
||||
@Parameter (name = ApiConstants.USE_STORAGE_REPLICATION,
|
||||
type=CommandType.BOOLEAN,
|
||||
since = "4.21.0",
|
||||
description = "Enables the snapshot to be copied to the supported primary storages when the config 'use.storage.replication' is set to true for the storage or globally. " +
|
||||
"This is supported only for StorPool storage for now.")
|
||||
protected Boolean useStorageReplication;
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
|
|
|
|||
|
|
@ -61,6 +61,16 @@ public class BackupOfferingResponse extends BaseResponse {
|
|||
@Param(description = "Zone name")
|
||||
private String zoneName;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN_ID)
|
||||
@Param(description = "the domain ID(s) this backup offering belongs to.",
|
||||
since = "4.23.0")
|
||||
private String domainId;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN)
|
||||
@Param(description = "the domain name(s) this backup offering belongs to.",
|
||||
since = "4.23.0")
|
||||
private String domain;
|
||||
|
||||
@SerializedName(ApiConstants.CROSS_ZONE_INSTANCE_CREATION)
|
||||
@Param(description = "the backups with this offering can be used to create Instances on all Zones", since = "4.22.0")
|
||||
private Boolean crossZoneInstanceCreation;
|
||||
|
|
@ -108,4 +118,13 @@ public class BackupOfferingResponse extends BaseResponse {
|
|||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public void setDomainId(String domainId) {
|
||||
this.domainId = domainId;
|
||||
}
|
||||
|
||||
public void setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ public class BackupRepositoryResponse extends BaseResponse {
|
|||
@Param(description = "backup type")
|
||||
private String type;
|
||||
|
||||
@SerializedName(ApiConstants.MOUNT_OPTIONS)
|
||||
@Param(description = "mount options", since = "4.22.1")
|
||||
private String mountOptions;
|
||||
|
||||
@SerializedName(ApiConstants.CAPACITY_BYTES)
|
||||
@Param(description = "capacity of the backup repository")
|
||||
private Long capacityBytes;
|
||||
|
|
@ -128,6 +132,14 @@ public class BackupRepositoryResponse extends BaseResponse {
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
public String getMountOptions() {
|
||||
return mountOptions;
|
||||
}
|
||||
|
||||
public void setMountOptions(String mountOptions) {
|
||||
this.mountOptions = mountOptions;
|
||||
}
|
||||
|
||||
public Long getCapacityBytes() {
|
||||
return capacityBytes;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.api.response;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
|
|
@ -153,6 +155,10 @@ public class CapabilitiesResponse extends BaseResponse {
|
|||
@Param(description = "true if additional configurations or extraconfig can be passed to Instances", since = "4.20.2")
|
||||
private Boolean additionalConfigEnabled;
|
||||
|
||||
@SerializedName(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS)
|
||||
@Param(description = "Excluded and obsolete VPN customer gateway cryptographic parameters")
|
||||
private Map<String, Object> vpnCustomerGatewayParameters;
|
||||
|
||||
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
|
||||
this.securityGroupsEnabled = securityGroupsEnabled;
|
||||
}
|
||||
|
|
@ -280,4 +286,8 @@ public class CapabilitiesResponse extends BaseResponse {
|
|||
public void setAdditionalConfigEnabled(Boolean additionalConfigEnabled) {
|
||||
this.additionalConfigEnabled = additionalConfigEnabled;
|
||||
}
|
||||
|
||||
public void setVpnCustomerGatewayParameters(Map<String, Object> vpnCustomerGatewayParameters) {
|
||||
this.vpnCustomerGatewayParameters = vpnCustomerGatewayParameters;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,14 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotation
|
|||
@Param(description = "Which IKE Version to use, one of ike (autoselect), IKEv1, or IKEv2. Defaults to ike")
|
||||
private String ikeVersion;
|
||||
|
||||
@SerializedName(ApiConstants.OBSOLETE_PARAMETERS)
|
||||
@Param(description = "Contains the list of obsolete/insecure cryptographic parameters that the vpn customer gateway is using.", since = "4.23.0")
|
||||
private String obsoleteParameters;
|
||||
|
||||
@SerializedName(ApiConstants.EXCLUDED_PARAMETERS)
|
||||
@Param(description = "Contains the list of excluded/not allowed cryptographic parameters that the vpn customer gateway is using.", since = "4.23.0")
|
||||
private String excludedParameters;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
|
@ -202,4 +210,12 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotation
|
|||
this.domainPath = domainPath;
|
||||
}
|
||||
|
||||
public void setContainsObsoleteParameters(String obsoleteParameters) {
|
||||
this.obsoleteParameters = obsoleteParameters;
|
||||
}
|
||||
|
||||
public void setContainsExcludedParameters(String excludedParameters) {
|
||||
this.excludedParameters = excludedParameters;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,8 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
|
|||
*/
|
||||
BackupOffering importBackupOffering(final ImportBackupOfferingCmd cmd);
|
||||
|
||||
List<Long> getBackupOfferingDomains(final Long offeringId);
|
||||
|
||||
/**
|
||||
* Clone an existing backup offering with updated values
|
||||
* @param cmd clone backup offering cmd
|
||||
|
|
|
|||
|
|
@ -32,5 +32,4 @@ public interface BackupRepositoryService {
|
|||
BackupRepository updateBackupRepository(UpdateBackupRepositoryCmd cmd);
|
||||
boolean deleteBackupRepository(DeleteBackupRepositoryCmd cmd);
|
||||
Pair<List<BackupRepository>, Integer> listBackupRepositories(ListBackupRepositoriesCmd cmd);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
// 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.offering;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.LongFunction;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.NetworkRuleConflictException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DomainAndZoneIdResolverTest {
|
||||
static class TestCmd extends BaseCmd implements DomainAndZoneIdResolver {
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
|
||||
// No implementation needed for tests
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return 1L;
|
||||
}
|
||||
}
|
||||
|
||||
private void setEntityMgr(final BaseCmd cmd, final EntityManager entityMgr) throws Exception {
|
||||
Field f = BaseCmd.class.getDeclaredField("_entityMgr");
|
||||
f.setAccessible(true);
|
||||
f.set(cmd, entityMgr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveDomainIds_usesDefaultProviderWhenEmpty() {
|
||||
TestCmd cmd = new TestCmd();
|
||||
|
||||
final LongFunction<List<Long>> defaultsProvider = id -> Arrays.asList(100L, 200L);
|
||||
|
||||
List<Long> result = cmd.resolveDomainIds("", 42L, defaultsProvider, "offering");
|
||||
Assert.assertEquals(Arrays.asList(100L, 200L), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveDomainIds_resolvesValidUuids() throws Exception {
|
||||
TestCmd cmd = new TestCmd();
|
||||
|
||||
EntityManager em = mock(EntityManager.class);
|
||||
setEntityMgr(cmd, em);
|
||||
|
||||
Domain d1 = mock(Domain.class);
|
||||
when(d1.getId()).thenReturn(10L);
|
||||
Domain d2 = mock(Domain.class);
|
||||
when(d2.getId()).thenReturn(20L);
|
||||
|
||||
when(em.findByUuid(Domain.class, "uuid1")).thenReturn(d1);
|
||||
when(em.findByUuid(Domain.class, "uuid2")).thenReturn(d2);
|
||||
|
||||
List<Long> ids = cmd.resolveDomainIds("uuid1, public, uuid2", null, null, "template");
|
||||
Assert.assertEquals(Arrays.asList(10L, 20L), ids);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveDomainIds_invalidUuid_throws() throws Exception {
|
||||
TestCmd cmd = new TestCmd();
|
||||
|
||||
EntityManager em = mock(EntityManager.class);
|
||||
setEntityMgr(cmd, em);
|
||||
|
||||
when(em.findByUuid(Domain.class, "bad-uuid")).thenReturn(null);
|
||||
|
||||
Assert.assertThrows(InvalidParameterValueException.class,
|
||||
() -> cmd.resolveDomainIds("bad-uuid", null, null, "offering"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveZoneIds_usesDefaultProviderWhenEmpty() {
|
||||
TestCmd cmd = new TestCmd();
|
||||
|
||||
final LongFunction<List<Long>> defaultsProvider = id -> Collections.singletonList(300L);
|
||||
|
||||
List<Long> result = cmd.resolveZoneIds("", 99L, defaultsProvider, "offering");
|
||||
Assert.assertEquals(Collections.singletonList(300L), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveZoneIds_resolvesValidUuids() throws Exception {
|
||||
TestCmd cmd = new TestCmd();
|
||||
|
||||
EntityManager em = mock(EntityManager.class);
|
||||
setEntityMgr(cmd, em);
|
||||
|
||||
DataCenter z1 = mock(DataCenter.class);
|
||||
when(z1.getId()).thenReturn(30L);
|
||||
DataCenter z2 = mock(DataCenter.class);
|
||||
when(z2.getId()).thenReturn(40L);
|
||||
|
||||
when(em.findByUuid(DataCenter.class, "zone-1")).thenReturn(z1);
|
||||
when(em.findByUuid(DataCenter.class, "zone-2")).thenReturn(z2);
|
||||
|
||||
List<Long> ids = cmd.resolveZoneIds("zone-1, all, zone-2", null, null, "service");
|
||||
Assert.assertEquals(Arrays.asList(30L, 40L), ids);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveZoneIds_invalidUuid_throws() throws Exception {
|
||||
TestCmd cmd = new TestCmd();
|
||||
|
||||
EntityManager em = mock(EntityManager.class);
|
||||
setEntityMgr(cmd, em);
|
||||
|
||||
when(em.findByUuid(DataCenter.class, "bad-zone")).thenReturn(null);
|
||||
|
||||
Assert.assertThrows(InvalidParameterValueException.class,
|
||||
() -> cmd.resolveZoneIds("bad-zone", null, null, "offering"));
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,106 @@ from cloudutils.cloudException import CloudRuntimeException, CloudInternalExcept
|
|||
from cloudutils.globalEnv import globalEnv
|
||||
from cloudutils.serviceConfigServer import cloudManagementConfig
|
||||
from optparse import OptionParser
|
||||
import urllib.request
|
||||
import configparser
|
||||
import hashlib
|
||||
|
||||
SYSTEMVM_TEMPLATES_PATH = "/usr/share/cloudstack-management/templates/systemvm"
|
||||
SYSTEMVM_TEMPLATES_METADATA_FILE = SYSTEMVM_TEMPLATES_PATH + "/metadata.ini"
|
||||
|
||||
def verify_sha512_checksum(file_path, expected_checksum):
|
||||
sha512 = hashlib.sha512()
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(8192), b""):
|
||||
sha512.update(chunk)
|
||||
return sha512.hexdigest().lower() == expected_checksum.lower()
|
||||
except Exception as e:
|
||||
print(f"Failed to verify checksum for {file_path}: {e}")
|
||||
return False
|
||||
|
||||
def download_file(url, dest_path, chunk_size=8 * 1024 * 1024):
|
||||
"""
|
||||
Downloads a file from the given URL to the specified destination path in chunks.
|
||||
"""
|
||||
try:
|
||||
with urllib.request.urlopen(url) as response:
|
||||
total_size = response.length if response.length else None
|
||||
downloaded = 0
|
||||
try:
|
||||
with open(dest_path, 'wb') as out_file:
|
||||
while True:
|
||||
chunk = response.read(chunk_size)
|
||||
if not chunk:
|
||||
break
|
||||
out_file.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
if total_size:
|
||||
print(f"Downloaded {downloaded / (1024 * 1024):.2f}MB of {total_size / (1024 * 1024):.2f}MB", end='\r')
|
||||
except PermissionError as pe:
|
||||
print(f"Permission denied: {dest_path}")
|
||||
raise
|
||||
print(f"\nDownloaded file from {url} to {dest_path}")
|
||||
except Exception as e:
|
||||
print(f"Failed to download file: {e}")
|
||||
raise
|
||||
|
||||
def download_template_if_needed(template, url, filename, checksum):
|
||||
dest_path = os.path.join(SYSTEMVM_TEMPLATES_PATH, filename)
|
||||
if os.path.exists(dest_path):
|
||||
if checksum and verify_sha512_checksum(dest_path, checksum):
|
||||
print(f"{template} System VM template already exists at {dest_path} with valid checksum, skipping download.")
|
||||
return
|
||||
else:
|
||||
print(f"{template} System VM template at {dest_path} has invalid or missing checksum, re-downloading...")
|
||||
else:
|
||||
print(f"Downloading {template} System VM template from {url} to {dest_path}...")
|
||||
try:
|
||||
download_file(url, dest_path)
|
||||
#After download, verify checksum if provided
|
||||
if checksum:
|
||||
if verify_sha512_checksum(dest_path, checksum):
|
||||
print(f"{template} System VM template downloaded and verified successfully.")
|
||||
else:
|
||||
print(f"ERROR: Checksum verification failed for {template} System VM template after download.")
|
||||
except Exception as e:
|
||||
print(f"ERROR: Failed to download {template} System VM template: {e}")
|
||||
|
||||
def collect_template_metadata(selected_templates, options):
|
||||
template_metadata_list = []
|
||||
if not os.path.exists(SYSTEMVM_TEMPLATES_METADATA_FILE):
|
||||
print(f"ERROR: System VM templates metadata file not found at {SYSTEMVM_TEMPLATES_METADATA_FILE}, cannot download templates.")
|
||||
sys.exit(1)
|
||||
config = configparser.ConfigParser()
|
||||
config.read(SYSTEMVM_TEMPLATES_METADATA_FILE)
|
||||
template_repo_url = None
|
||||
if options.systemvm_templates_repository:
|
||||
if "default" in config and "downloadrepository" in config["default"]:
|
||||
template_repo_url = config["default"]["downloadrepository"].strip()
|
||||
if not template_repo_url:
|
||||
print("ERROR: downloadrepository value is empty in metadata file, cannot use --systemvm-template-repository option.")
|
||||
sys.exit(1)
|
||||
for template in selected_templates:
|
||||
if template in config:
|
||||
url = config[template].get("downloadurl")
|
||||
filename = config[template].get("filename")
|
||||
checksum = config[template].get("checksum")
|
||||
if url and filename:
|
||||
if template_repo_url:
|
||||
url = url.replace(template_repo_url, options.systemvm_templates_repository)
|
||||
template_metadata_list.append({
|
||||
"template": template,
|
||||
"url": url,
|
||||
"filename": filename,
|
||||
"checksum": checksum
|
||||
})
|
||||
else:
|
||||
print(f"ERROR: URL or filename not found for {template} System VM template in metadata.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"ERROR: No metadata found for {template} System VM template.")
|
||||
sys.exit(1)
|
||||
return template_metadata_list
|
||||
|
||||
if __name__ == '__main__':
|
||||
initLoging("@MSLOGDIR@/setupManagement.log")
|
||||
|
|
@ -45,6 +145,16 @@ if __name__ == '__main__':
|
|||
parser.add_option("--https", action="store_true", dest="https", help="Enable HTTPs connection of management server")
|
||||
parser.add_option("--tomcat7", action="store_true", dest="tomcat7", help="Depreciated option, don't use it")
|
||||
parser.add_option("--no-start", action="store_true", dest="nostart", help="Do not start management server after successful configuration")
|
||||
parser.add_option(
|
||||
"--systemvm-templates",
|
||||
dest="systemvm_templates",
|
||||
help="Specify System VM templates to download: all, kvm-aarch64, kvm-x86_64, xenserver, vmware or comma-separated list of hypervisor combinations (e.g., kvm-x86_64,xenserver). Default is kvm-x86_64.",
|
||||
)
|
||||
parser.add_option(
|
||||
"--systemvm-templates-repository",
|
||||
dest="systemvm_templates_repository",
|
||||
help="Specify the URL to download System VM templates from."
|
||||
)
|
||||
(options, args) = parser.parse_args()
|
||||
if options.https:
|
||||
glbEnv.svrMode = "HttpsServer"
|
||||
|
|
@ -53,6 +163,34 @@ if __name__ == '__main__':
|
|||
if options.nostart:
|
||||
glbEnv.noStart = True
|
||||
|
||||
available_templates = ["kvm-aarch64", "kvm-x86_64", "xenserver", "vmware"]
|
||||
templates_arg = options.systemvm_templates
|
||||
|
||||
selected_templates = ["kvm-x86_64"]
|
||||
if templates_arg:
|
||||
templates_list = [t.strip().lower() for t in templates_arg.split(",")]
|
||||
if "all" in templates_list:
|
||||
if len(templates_list) > 1:
|
||||
print("WARNING: 'all' specified for System VM templates, ignoring other specified templates.")
|
||||
selected_templates = available_templates
|
||||
else:
|
||||
invalid_templates = []
|
||||
for t in templates_list:
|
||||
if t in available_templates:
|
||||
if t not in selected_templates:
|
||||
selected_templates.append(t)
|
||||
else:
|
||||
if t not in invalid_templates:
|
||||
invalid_templates.append(t)
|
||||
if invalid_templates:
|
||||
print(f"ERROR: Invalid System VM template names provided: {', '.join(invalid_templates)}")
|
||||
sys.exit(1)
|
||||
print(f"Selected systemvm templates to download: {', '.join(selected_templates) if selected_templates else 'None'}")
|
||||
|
||||
template_metadata_list = []
|
||||
if selected_templates:
|
||||
template_metadata_list = collect_template_metadata(selected_templates, options)
|
||||
|
||||
glbEnv.mode = "Server"
|
||||
|
||||
print("Starting to configure CloudStack Management Server:")
|
||||
|
|
@ -74,3 +212,6 @@ if __name__ == '__main__':
|
|||
syscfg.restore()
|
||||
except:
|
||||
pass
|
||||
|
||||
for meta in template_metadata_list:
|
||||
download_template_if_needed(meta["template"], meta["url"], meta["filename"], meta["checksum"])
|
||||
|
|
|
|||
|
|
@ -62,3 +62,8 @@ extensions.deployment.mode=@EXTENSIONSDEPLOYMENTMODE@
|
|||
# Thread pool configuration
|
||||
#threads.min=10
|
||||
#threads.max=500
|
||||
|
||||
# The URL prefix for the system VM templates repository. When downloading system VM templates, the server replaces the
|
||||
# `downloadrepository` key value from the metadata file in template URLs. If not specified, the original template URL
|
||||
# will be used for download.
|
||||
# system.vm.templates.download.repository=http://download.cloudstack.org/systemvm/
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te
|
|||
client.getParams().setAuthenticationPreemptive(true);
|
||||
Credentials defaultcreds = new UsernamePasswordCredentials(user, password);
|
||||
client.getState().setCredentials(new AuthScope(hostAndPort.first(), hostAndPort.second(), AuthScope.ANY_REALM), defaultcreds);
|
||||
logger.info("Added username=" + user + ", password=" + password + "for host " + hostAndPort.first() + ":" + hostAndPort.second());
|
||||
logger.info("Added username={}, password=****** for host {}:{}", user, hostAndPort.first(), hostAndPort.second());
|
||||
} else {
|
||||
logger.info("No credentials configured for host=" + hostAndPort.first() + ":" + hostAndPort.second());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ public interface ObjectInDataStoreStateMachine extends StateObject<ObjectInDataS
|
|||
Failed("Failed to download Template"),
|
||||
Hidden("The object is hidden from the user");
|
||||
|
||||
String _description;
|
||||
final String _description;
|
||||
|
||||
private State(String description) {
|
||||
State(String description) {
|
||||
_description = description;
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ public interface ObjectInDataStoreStateMachine extends StateObject<ObjectInDataS
|
|||
CreateRequested,
|
||||
CreateOnlyRequested,
|
||||
DestroyRequested,
|
||||
OperationSuccessed,
|
||||
OperationSucceeded,
|
||||
OperationFailed,
|
||||
CopyRequested,
|
||||
CopyingRequested,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ public interface SnapshotDataFactory {
|
|||
|
||||
SnapshotInfo getSnapshot(long snapshotId, long storeId, DataStoreRole role);
|
||||
|
||||
SnapshotInfo getSnapshotIncludingRemoved(long snapshotId, long storeId, DataStoreRole role);
|
||||
|
||||
SnapshotInfo getSnapshotWithRoleAndZone(long snapshotId, DataStoreRole role, long zoneId);
|
||||
|
||||
SnapshotInfo getSnapshotOnPrimaryStore(long snapshotId);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,11 @@ public class PublicIp implements PublicIpAddress {
|
|||
}
|
||||
|
||||
public static PublicIp createFromAddrAndVlan(IPAddressVO addr, VlanVO vlan) {
|
||||
return new PublicIp(addr, vlan, NetUtils.createSequenceBasedMacAddress(addr.getMacAddress(), NetworkModel.MACIdentifier.value()));
|
||||
long macIdentifier = NetworkModel.MACIdentifier.valueIn(addr.getDataCenterId());
|
||||
if (macIdentifier == 0) {
|
||||
macIdentifier = addr.getDataCenterId();
|
||||
}
|
||||
return new PublicIp(addr, vlan, NetUtils.createSequenceBasedMacAddress(addr.getMacAddress(), macIdentifier));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -279,5 +283,4 @@ public class PublicIp implements PublicIpAddress {
|
|||
public boolean isForRouter() {
|
||||
return _addr.isForRouter();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3053,7 +3053,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
}
|
||||
|
||||
protected void migrate(final VMInstanceVO vm, final long srcHostId, final DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException {
|
||||
logger.info("Migrating {} to {}", vm, dest);
|
||||
logger.info("Start preparing migration of the VM: {} to {}", vm, dest);
|
||||
final long dstHostId = dest.getHost().getId();
|
||||
final Host fromHost = _hostDao.findById(srcHostId);
|
||||
if (fromHost == null) {
|
||||
|
|
@ -3118,9 +3118,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
if (pfma == null || !pfma.getResult()) {
|
||||
final String details = pfma != null ? pfma.getDetails() : "null answer returned";
|
||||
final String msg = "Unable to prepare for migration due to " + details;
|
||||
logger.error("Failed to prepare destination host {} for migration of VM {} : {}", dstHostId, vm.getInstanceName(), details);
|
||||
pfma = null;
|
||||
throw new AgentUnavailableException(msg, dstHostId);
|
||||
}
|
||||
logger.debug("Successfully prepared destination host {} for migration of VM {} ", dstHostId, vm.getInstanceName());
|
||||
} catch (final OperationTimedoutException e1) {
|
||||
throw new AgentUnavailableException("Operation timed out", dstHostId);
|
||||
} finally {
|
||||
|
|
@ -3141,18 +3143,23 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
volumeMgr.release(vm.getId(), dstHostId);
|
||||
}
|
||||
|
||||
logger.info("Migration cancelled because state has changed: {}", vm);
|
||||
throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm);
|
||||
String msg = "Migration cancelled because state has changed: " + vm;
|
||||
logger.warn(msg);
|
||||
throw new ConcurrentOperationException(msg);
|
||||
}
|
||||
} catch (final NoTransitionException e1) {
|
||||
_networkMgr.rollbackNicForMigration(vmSrc, profile);
|
||||
volumeMgr.release(vm.getId(), dstHostId);
|
||||
logger.info("Migration cancelled because {}", e1.getMessage());
|
||||
String msg = String.format("Migration cancelled for VM %s due to state transition failure: %s",
|
||||
vm.getInstanceName(), e1.getMessage());
|
||||
logger.warn(msg, e1);
|
||||
throw new ConcurrentOperationException("Migration cancelled because " + e1.getMessage());
|
||||
} catch (final CloudRuntimeException e2) {
|
||||
_networkMgr.rollbackNicForMigration(vmSrc, profile);
|
||||
volumeMgr.release(vm.getId(), dstHostId);
|
||||
logger.info("Migration cancelled because {}", e2.getMessage());
|
||||
String msg = String.format("Migration cancelled for VM %s due to runtime exception: %s",
|
||||
vm.getInstanceName(), e2.getMessage());
|
||||
logger.error(msg, e2);
|
||||
work.setStep(Step.Done);
|
||||
_workDao.update(work.getId(), work);
|
||||
try {
|
||||
|
|
@ -3172,8 +3179,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);
|
||||
if (ma == null || !ma.getResult()) {
|
||||
final String details = ma != null ? ma.getDetails() : "null answer returned";
|
||||
String msg = String.format("Migration command failed for VM %s on source host id=%s to destination host %s: %s",
|
||||
vm.getInstanceName(), vm.getLastHostId(), dstHostId, details);
|
||||
logger.error(msg);
|
||||
throw new CloudRuntimeException(details);
|
||||
}
|
||||
logger.info("Migration command successful for VM {}", vm.getInstanceName());
|
||||
} catch (final OperationTimedoutException e) {
|
||||
boolean success = false;
|
||||
if (HypervisorType.KVM.equals(vm.getHypervisorType())) {
|
||||
|
|
@ -3210,7 +3221,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
|
||||
try {
|
||||
if (!checkVmOnHost(vm, dstHostId)) {
|
||||
logger.error("Unable to complete migration for {}", vm);
|
||||
logger.error("Migration verification failed for VM {} : VM not found on destination host {} ", vm.getInstanceName(), dstHostId);
|
||||
try {
|
||||
_agentMgr.send(srcHostId, new Commands(cleanup(vm, dpdkInterfaceMapping)), null);
|
||||
} catch (final AgentUnavailableException e) {
|
||||
|
|
@ -3225,7 +3236,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
migrated = true;
|
||||
} finally {
|
||||
if (!migrated) {
|
||||
logger.info("Migration was unsuccessful. Cleaning up: {}", vm);
|
||||
logger.info("Migration was unsuccessful. Cleaning up: {}", vm);
|
||||
_networkMgr.rollbackNicForMigration(vmSrc, profile);
|
||||
volumeMgr.release(vm.getId(), dstHostId);
|
||||
// deallocate GPU devices for the VM on the destination host
|
||||
|
|
@ -3237,7 +3248,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
try {
|
||||
_agentMgr.send(dstHostId, new Commands(cleanup(vm, dpdkInterfaceMapping)), null);
|
||||
} catch (final AgentUnavailableException ae) {
|
||||
logger.warn("Looks like the destination Host is unavailable for cleanup", ae);
|
||||
logger.warn("Destination host {} unavailable for cleanup after failed migration of VM {}", dstHostId, vm.getInstanceName(), ae);
|
||||
}
|
||||
_networkMgr.setHypervisorHostname(profile, dest, false);
|
||||
try {
|
||||
|
|
@ -3246,6 +3257,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
logger.warn(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.info("Migration completed successfully for VM %s" + vm);
|
||||
_networkMgr.commitNicForMigration(vmSrc, profile);
|
||||
volumeMgr.release(vm.getId(), srcHostId);
|
||||
// deallocate GPU devices for the VM on the src host after migration is complete
|
||||
|
|
@ -3276,6 +3288,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||
migrateCommand.setVlanToPersistenceMap(vlanToPersistenceMap);
|
||||
}
|
||||
|
||||
logger.debug("Setting auto convergence to: {}", StorageManager.KvmAutoConvergence.value());
|
||||
migrateCommand.setAutoConvergence(StorageManager.KvmAutoConvergence.value());
|
||||
migrateCommand.setHostGuid(destination.getHost().getGuid());
|
||||
|
||||
|
|
|
|||
|
|
@ -1283,7 +1283,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
|||
nicProfile.setIPv4Gateway(ipv4Gateway);
|
||||
nicProfile.setIPv4Netmask(ipv4Netmask);
|
||||
|
||||
if (nicProfile.getMacAddress() == null) {
|
||||
if (nicProfile.getMacAddress() == null || !_networkModel.isMACUnique(nicProfile.getMacAddress(), network.getId())) {
|
||||
try {
|
||||
String macAddress = _networkModel.getNextAvailableMacAddressInNetwork(network.getId());
|
||||
nicProfile.setMacAddress(macAddress);
|
||||
|
|
|
|||
|
|
@ -396,6 +396,7 @@ public class NetworkOrchestratorTest extends TestCase {
|
|||
when(testOrchestrator._ipAddressDao.acquireInLockTable(Mockito.anyLong())).thenReturn(ipVoSpy);
|
||||
when(testOrchestrator._ipAddressDao.update(Mockito.anyLong(), Mockito.any(IPAddressVO.class))).thenReturn(true);
|
||||
when(testOrchestrator._ipAddressDao.releaseFromLockTable(Mockito.anyLong())).thenReturn(true);
|
||||
when(testOrchestrator._networkModel.isMACUnique(Mockito.anyString(), Mockito.anyLong())).thenReturn(true);
|
||||
try {
|
||||
when(testOrchestrator._networkModel.getNextAvailableMacAddressInNetwork(Mockito.anyLong())).thenReturn(macAddress);
|
||||
} catch (InsufficientAddressCapacityException e) {
|
||||
|
|
|
|||
|
|
@ -213,6 +213,8 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||
|
||||
private static final String LEFT_JOIN_VM_TEMPLATE = "LEFT JOIN vm_template ON vm_template.id = vi.vm_template_id ";
|
||||
|
||||
private static final String STORAGE_POOLS_WITH_CHILDREN = "SELECT DISTINCT parent FROM storage_pool WHERE parent != 0 AND removed IS NULL";
|
||||
|
||||
public CapacityDaoImpl() {
|
||||
_hostIdTypeSearch = createSearchBuilder();
|
||||
_hostIdTypeSearch.and("hostId", _hostIdTypeSearch.entity().getHostOrPoolId(), SearchCriteria.Op.EQ);
|
||||
|
|
@ -379,6 +381,11 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||
finalQuery.append(" AND capacity_type = ?");
|
||||
resourceIdList.add(capacityType.longValue());
|
||||
}
|
||||
|
||||
// Exclude storage pools with children from capacity calculations to avoid double counting
|
||||
finalQuery.append(" AND NOT (capacity.capacity_type = ").append(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED)
|
||||
.append(" AND capacity.host_id IN (").append(STORAGE_POOLS_WITH_CHILDREN).append("))");
|
||||
|
||||
if (CollectionUtils.isNotEmpty(hostIds)) {
|
||||
finalQuery.append(String.format(" AND capacity.host_id IN (%s)", StringUtils.join(hostIds, ",")));
|
||||
if (capacityType == null) {
|
||||
|
|
@ -541,6 +548,10 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||
StringBuilder sql = new StringBuilder(LIST_CAPACITY_GROUP_BY_CAPACITY_PART1);
|
||||
List<Long> resourceIdList = new ArrayList<Long>();
|
||||
|
||||
// Exclude storage pools with children from capacity calculations to avoid double counting
|
||||
sql.append(" AND NOT (capacity.capacity_type = ").append(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED)
|
||||
.append(" AND capacity.host_id IN (").append(STORAGE_POOLS_WITH_CHILDREN).append("))");
|
||||
|
||||
if (zoneId != null) {
|
||||
sql.append(" AND capacity.data_center_id = ?");
|
||||
resourceIdList.add(zoneId);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ public class DataCenterDetailsDaoImpl extends ResourceDetailsDaoBase<DataCenterD
|
|||
|
||||
private final SearchBuilder<DataCenterDetailVO> DetailSearch;
|
||||
|
||||
DataCenterDetailsDaoImpl() {
|
||||
public DataCenterDetailsDaoImpl() {
|
||||
super();
|
||||
DetailSearch = createSearchBuilder();
|
||||
DetailSearch.and("zoneId", DetailSearch.entity().getResourceId(), SearchCriteria.Op.EQ);
|
||||
DetailSearch.and("name", DetailSearch.entity().getName(), SearchCriteria.Op.EQ);
|
||||
|
|
|
|||
|
|
@ -462,8 +462,8 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
|
|||
public String getNextAvailableMacAddress(final long networkConfigId, Integer zoneMacIdentifier) {
|
||||
final SequenceFetcher fetch = SequenceFetcher.getInstance();
|
||||
long seq = fetch.getNextSequence(Long.class, _tgMacAddress, networkConfigId);
|
||||
if(zoneMacIdentifier != null && zoneMacIdentifier.intValue() != 0 ){
|
||||
seq = seq | _prefix << 40 | (long)zoneMacIdentifier << 32 | networkConfigId << 16 & 0x00000000ffff0000l;
|
||||
if (zoneMacIdentifier != null && zoneMacIdentifier != 0) {
|
||||
seq = seq | _prefix << 40 | (long)zoneMacIdentifier << 32 | networkConfigId << 16 & 0x00000000ffff0000L;
|
||||
}
|
||||
return NetUtils.long2Mac(seq);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ public interface SnapshotDao extends GenericDao<SnapshotVO, Long>, StateDao<Snap
|
|||
|
||||
List<SnapshotVO> listAllByStatus(Snapshot.State... status);
|
||||
|
||||
List<SnapshotVO> listAllByStatusIncludingRemoved(Snapshot.State... status);
|
||||
|
||||
void updateVolumeIds(long oldVolId, long newVolId);
|
||||
|
||||
List<SnapshotVO> listByStatusNotIn(long volumeId, Snapshot.State... status);
|
||||
|
|
|
|||
|
|
@ -265,6 +265,13 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
|
|||
return listBy(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotVO> listAllByStatusIncludingRemoved(Snapshot.State... status) {
|
||||
SearchCriteria<SnapshotVO> sc = StatusSearch.create();
|
||||
sc.setParameters("status", (Object[])status);
|
||||
return listIncludingRemovedBy(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotVO> listByIds(Object... ids) {
|
||||
SearchCriteria<SnapshotVO> sc = snapshotIdsSearch.create();
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long>, StateDao<
|
|||
|
||||
List<VMTemplateVO> listByParentTemplatetId(long parentTemplatetId);
|
||||
|
||||
VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch);
|
||||
VMTemplateVO findLatestTemplateByName(String name, HypervisorType hypervisorType, CPU.CPUArch arch);
|
||||
|
||||
List<VMTemplateVO> findTemplatesLinkedToUserdata(long userdataId);
|
||||
|
||||
|
|
@ -103,4 +103,7 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long>, StateDao<
|
|||
List<Long> listIdsByTemplateTag(String tag);
|
||||
|
||||
List<Long> listIdsByExtensionId(long extensionId);
|
||||
|
||||
VMTemplateVO findActiveSystemTemplateByHypervisorArchAndUrlPath(HypervisorType hypervisorType,
|
||||
CPU.CPUArch arch, String urlPathSuffix);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,13 +245,17 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
|
|||
|
||||
|
||||
@Override
|
||||
public VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch) {
|
||||
public VMTemplateVO findLatestTemplateByName(String name, HypervisorType hypervisorType, CPU.CPUArch arch) {
|
||||
SearchBuilder<VMTemplateVO> sb = createSearchBuilder();
|
||||
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
|
||||
sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ);
|
||||
sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
SearchCriteria<VMTemplateVO> sc = sb.create();
|
||||
sc.setParameters("name", name);
|
||||
if (hypervisorType != null) {
|
||||
sc.setParameters("hypervisorType", hypervisorType);
|
||||
}
|
||||
if (arch != null) {
|
||||
sc.setParameters("arch", arch);
|
||||
}
|
||||
|
|
@ -850,6 +854,37 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
|
|||
return customSearch(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMTemplateVO findActiveSystemTemplateByHypervisorArchAndUrlPath(HypervisorType hypervisorType,
|
||||
CPU.CPUArch arch, String urlPathSuffix) {
|
||||
if (StringUtils.isBlank(urlPathSuffix)) {
|
||||
return null;
|
||||
}
|
||||
SearchBuilder<VMTemplateVO> sb = createSearchBuilder();
|
||||
sb.and("templateType", sb.entity().getTemplateType(), SearchCriteria.Op.EQ);
|
||||
sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ);
|
||||
sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ);
|
||||
sb.and("urlPathSuffix", sb.entity().getUrl(), SearchCriteria.Op.LIKE);
|
||||
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
SearchCriteria<VMTemplateVO> sc = sb.create();
|
||||
sc.setParameters("templateType", TemplateType.SYSTEM);
|
||||
if (hypervisorType != null) {
|
||||
sc.setParameters("hypervisorType", hypervisorType);
|
||||
}
|
||||
if (arch != null) {
|
||||
sc.setParameters("arch", arch);
|
||||
}
|
||||
sc.setParameters("urlPathSuffix", "%" + urlPathSuffix);
|
||||
sc.setParameters("state", VirtualMachineTemplate.State.Active);
|
||||
Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L);
|
||||
List<VMTemplateVO> templates = listBy(sc, filter);
|
||||
if (CollectionUtils.isNotEmpty(templates)) {
|
||||
return templates.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateState(
|
||||
com.cloud.template.VirtualMachineTemplate.State currentState,
|
||||
|
|
|
|||
|
|
@ -163,5 +163,7 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.S
|
|||
|
||||
VolumeVO findOneByIScsiName(String iScsiName);
|
||||
|
||||
int getVolumeCountByOfferingId(long diskOfferingId);
|
||||
|
||||
VolumeVO findByLastIdAndState(long lastVolumeId, Volume.State...states);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
protected GenericSearchBuilder<VolumeVO, SumCount> primaryStorageSearch2;
|
||||
protected GenericSearchBuilder<VolumeVO, SumCount> secondaryStorageSearch;
|
||||
private final SearchBuilder<VolumeVO> poolAndPathSearch;
|
||||
final GenericSearchBuilder<VolumeVO, Integer> CountByOfferingId;
|
||||
|
||||
@Inject
|
||||
ReservationDao reservationDao;
|
||||
|
|
@ -506,6 +507,11 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
poolAndPathSearch.and("poolId", poolAndPathSearch.entity().getPoolId(), Op.EQ);
|
||||
poolAndPathSearch.and("path", poolAndPathSearch.entity().getPath(), Op.EQ);
|
||||
poolAndPathSearch.done();
|
||||
|
||||
CountByOfferingId = createSearchBuilder(Integer.class);
|
||||
CountByOfferingId.select(null, Func.COUNT, CountByOfferingId.entity().getId());
|
||||
CountByOfferingId.and("diskOfferingId", CountByOfferingId.entity().getDiskOfferingId(), Op.EQ);
|
||||
CountByOfferingId.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -914,6 +920,14 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
|
|||
return findOneIncludingRemovedBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVolumeCountByOfferingId(long diskOfferingId) {
|
||||
SearchCriteria<Integer> sc = CountByOfferingId.create();
|
||||
sc.setParameters("diskOfferingId", diskOfferingId);
|
||||
List<Integer> results = customSearch(sc, null);
|
||||
return results.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VolumeVO findByLastIdAndState(long lastVolumeId, State ...states) {
|
||||
QueryBuilder<VolumeVO> sc = QueryBuilder.create(VolumeVO.class);
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ public class DatabaseCreator {
|
|||
String username = dbProperties.getProperty(String.format("db.%s.username", database));
|
||||
String password = dbProperties.getProperty(String.format("db.%s.password", database));
|
||||
String dbName = dbProperties.getProperty(String.format("db.%s.name", database));
|
||||
System.out.println(String.format("========> Initializing database=%s with host=%s port=%s username=%s password=%s", dbName, host, port, username, password));
|
||||
System.out.println(String.format("========> Initializing database=%s with host=%s port=%s username=%s password=******", dbName, host, port, username));
|
||||
|
||||
List<String> queries = new ArrayList<String>();
|
||||
queries.add(String.format("drop database if exists `%s`", dbName));
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -52,7 +52,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
|
|||
private static final String DELETE_ALL = "DELETE FROM cloud_usage";
|
||||
private static final String DELETE_ALL_BY_ACCOUNTID = "DELETE FROM cloud_usage WHERE account_id = ?";
|
||||
private static final String DELETE_ALL_BY_INTERVAL = "DELETE FROM cloud_usage WHERE end_date < DATE_SUB(CURRENT_DATE(), INTERVAL ? DAY)";
|
||||
private static final String INSERT_ACCOUNT = "INSERT INTO cloud_usage.account (id, account_name, type, role_id, domain_id, removed, cleanup_needed) VALUES (?,?,?,?,?,?,?)";
|
||||
private static final String INSERT_ACCOUNT = "INSERT INTO cloud_usage.account (id, account_name, uuid, type, role_id, domain_id, removed, cleanup_needed) VALUES (?,?,?,?,?,?,?,?)";
|
||||
private static final String INSERT_USER_STATS = "INSERT INTO cloud_usage.user_statistics (id, data_center_id, account_id, public_ip_address, device_id, device_type, network_id, net_bytes_received,"
|
||||
+ " net_bytes_sent, current_bytes_received, current_bytes_sent, agg_bytes_received, agg_bytes_sent) VALUES (?,?,?,?,?,?,?,?,?,?, ?, ?, ?)";
|
||||
|
||||
|
|
@ -129,25 +129,26 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
|
|||
for (AccountVO acct : accounts) {
|
||||
pstmt.setLong(1, acct.getId());
|
||||
pstmt.setString(2, acct.getAccountName());
|
||||
pstmt.setInt(3, acct.getType().ordinal());
|
||||
pstmt.setString(3, acct.getUuid());
|
||||
pstmt.setInt(4, acct.getType().ordinal());
|
||||
|
||||
//prevent autoboxing NPE by defaulting to User role
|
||||
if(acct.getRoleId() == null){
|
||||
pstmt.setLong(4, RoleType.User.getId());
|
||||
pstmt.setLong(5, RoleType.User.getId());
|
||||
}else{
|
||||
pstmt.setLong(4, acct.getRoleId());
|
||||
pstmt.setLong(5, acct.getRoleId());
|
||||
}
|
||||
|
||||
pstmt.setLong(5, acct.getDomainId());
|
||||
pstmt.setLong(6, acct.getDomainId());
|
||||
|
||||
Date removed = acct.getRemoved();
|
||||
if (removed == null) {
|
||||
pstmt.setString(6, null);
|
||||
pstmt.setString(7, null);
|
||||
} else {
|
||||
pstmt.setString(6, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), acct.getRemoved()));
|
||||
pstmt.setString(7, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), acct.getRemoved()));
|
||||
}
|
||||
|
||||
pstmt.setBoolean(7, acct.getNeedsCleanup());
|
||||
pstmt.setBoolean(8, acct.getNeedsCleanup());
|
||||
|
||||
pstmt.addBatch();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ public interface NicDao extends GenericDao<NicVO, Long> {
|
|||
|
||||
List<NicVO> listByVmIdAndKeyword(long instanceId, String keyword);
|
||||
|
||||
NicVO findByMacAddress(String macAddress);
|
||||
NicVO findByMacAddress(String macAddress, long networkId);
|
||||
|
||||
NicVO findByNetworkIdAndMacAddressIncludingRemoved(long networkId, String mac);
|
||||
|
||||
|
|
|
|||
|
|
@ -420,9 +420,10 @@ public class NicDaoImpl extends GenericDaoBase<NicVO, Long> implements NicDao {
|
|||
}
|
||||
|
||||
@Override
|
||||
public NicVO findByMacAddress(String macAddress) {
|
||||
public NicVO findByMacAddress(String macAddress, long networkId) {
|
||||
SearchCriteria<NicVO> sc = AllFieldsSearch.create();
|
||||
sc.setParameters("macAddress", macAddress);
|
||||
sc.setParameters("network", networkId);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -187,5 +187,9 @@ public interface VMInstanceDao extends GenericDao<VMInstanceVO, Long>, StateDao<
|
|||
|
||||
Map<String, Long> getNameIdMapForVmIds(Collection<Long> ids);
|
||||
|
||||
int getVmCountByOfferingId(Long serviceOfferingId);
|
||||
|
||||
int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List<Long> domainIds);
|
||||
|
||||
List<VMInstanceVO> listByIdsIncludingRemoved(List<Long> ids);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,8 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
|
|||
protected SearchBuilder<VMInstanceVO> LastHostAndStatesSearch;
|
||||
protected SearchBuilder<VMInstanceVO> VmsNotInClusterUsingPool;
|
||||
protected SearchBuilder<VMInstanceVO> IdsPowerStateSelectSearch;
|
||||
GenericSearchBuilder<VMInstanceVO, Integer> CountByOfferingId;
|
||||
GenericSearchBuilder<VMInstanceVO, Integer> CountUserVmNotInDomain;
|
||||
|
||||
@Inject
|
||||
ResourceTagDao tagsDao;
|
||||
|
|
@ -354,6 +356,18 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
|
|||
IdsPowerStateSelectSearch.entity().getPowerStateUpdateCount(),
|
||||
IdsPowerStateSelectSearch.entity().getPowerStateUpdateTime());
|
||||
IdsPowerStateSelectSearch.done();
|
||||
|
||||
CountByOfferingId = createSearchBuilder(Integer.class);
|
||||
CountByOfferingId.select(null, Func.COUNT, CountByOfferingId.entity().getId());
|
||||
CountByOfferingId.and("serviceOfferingId", CountByOfferingId.entity().getServiceOfferingId(), Op.EQ);
|
||||
CountByOfferingId.done();
|
||||
|
||||
CountUserVmNotInDomain = createSearchBuilder(Integer.class);
|
||||
CountUserVmNotInDomain.select(null, Func.COUNT, CountUserVmNotInDomain.entity().getId());
|
||||
CountUserVmNotInDomain.and("serviceOfferingId", CountUserVmNotInDomain.entity().getServiceOfferingId(), Op.EQ);
|
||||
CountUserVmNotInDomain.and("domainIdsNotIn", CountUserVmNotInDomain.entity().getDomainId(), Op.NIN);
|
||||
CountUserVmNotInDomain.done();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1247,6 +1261,29 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
|
|||
.collect(Collectors.toMap(VMInstanceVO::getInstanceName, VMInstanceVO::getId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVmCountByOfferingId(Long serviceOfferingId) {
|
||||
if (serviceOfferingId == null) {
|
||||
return 0;
|
||||
}
|
||||
SearchCriteria<Integer> sc = CountByOfferingId.create();
|
||||
sc.setParameters("serviceOfferingId", serviceOfferingId);
|
||||
List<Integer> count = customSearch(sc, null);
|
||||
return count.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List<Long> domainIds) {
|
||||
if (serviceOfferingId == null || CollectionUtils.isEmpty(domainIds)) {
|
||||
return 0;
|
||||
}
|
||||
SearchCriteria<Integer> sc = CountUserVmNotInDomain.create();
|
||||
sc.setParameters("serviceOfferingId", serviceOfferingId);
|
||||
sc.setParameters("domainIdsNotIn", domainIds.toArray());
|
||||
List<Integer> count = customSearch(sc, null);
|
||||
return count.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VMInstanceVO> listByIdsIncludingRemoved(List<Long> ids) {
|
||||
SearchBuilder<VMInstanceVO> idsSearch = createSearchBuilder();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
// 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.backup;
|
||||
|
||||
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 org.apache.cloudstack.api.ResourceDetail;
|
||||
|
||||
@Entity
|
||||
@Table(name = "backup_offering_details")
|
||||
public class BackupOfferingDetailsVO implements ResourceDetail {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private long id;
|
||||
|
||||
@Column(name = "backup_offering_id")
|
||||
private long resourceId;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "value")
|
||||
private String value;
|
||||
|
||||
@Column(name = "display")
|
||||
private boolean display = true;
|
||||
|
||||
protected BackupOfferingDetailsVO() {
|
||||
}
|
||||
|
||||
public BackupOfferingDetailsVO(long backupOfferingId, String name, String value, boolean display) {
|
||||
this.resourceId = backupOfferingId;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(long backupOfferingId) {
|
||||
this.resourceId = backupOfferingId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisplay() {
|
||||
return display;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package org.apache.cloudstack.backup;
|
||||
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -131,4 +133,9 @@ public class BackupOfferingVO implements BackupOffering {
|
|||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Backup offering %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "name", "uuid"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ package org.apache.cloudstack.backup.dao;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.domain.DomainVO;
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
import org.apache.cloudstack.api.response.BackupOfferingResponse;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.backup.BackupOfferingVO;
|
||||
|
|
@ -30,10 +32,16 @@ import com.cloud.utils.db.GenericDaoBase;
|
|||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BackupOfferingDaoImpl extends GenericDaoBase<BackupOfferingVO, Long> implements BackupOfferingDao {
|
||||
|
||||
@Inject
|
||||
DataCenterDao dataCenterDao;
|
||||
@Inject
|
||||
BackupOfferingDetailsDao backupOfferingDetailsDao;
|
||||
@Inject
|
||||
DomainDao domainDao;
|
||||
|
||||
private SearchBuilder<BackupOfferingVO> backupPoliciesSearch;
|
||||
|
||||
|
|
@ -51,8 +59,9 @@ public class BackupOfferingDaoImpl extends GenericDaoBase<BackupOfferingVO, Long
|
|||
|
||||
@Override
|
||||
public BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering, Boolean crossZoneInstanceCreation) {
|
||||
DataCenterVO zone = dataCenterDao.findById(offering.getZoneId());
|
||||
|
||||
DataCenterVO zone = dataCenterDao.findById(offering.getZoneId());
|
||||
List<Long> domainIds = backupOfferingDetailsDao.findDomainIds(offering.getId());
|
||||
BackupOfferingResponse response = new BackupOfferingResponse();
|
||||
response.setId(offering.getUuid());
|
||||
response.setName(offering.getName());
|
||||
|
|
@ -64,6 +73,18 @@ public class BackupOfferingDaoImpl extends GenericDaoBase<BackupOfferingVO, Long
|
|||
response.setZoneId(zone.getUuid());
|
||||
response.setZoneName(zone.getName());
|
||||
}
|
||||
if (domainIds != null && !domainIds.isEmpty()) {
|
||||
String domainUUIDs = domainIds.stream().map(Long::valueOf).map(domainId -> {
|
||||
DomainVO domain = domainDao.findById(domainId);
|
||||
return domain != null ? domain.getUuid() : "";
|
||||
}).filter(name -> !name.isEmpty()).reduce((a, b) -> a + "," + b).orElse("");
|
||||
String domainNames = domainIds.stream().map(Long::valueOf).map(domainId -> {
|
||||
DomainVO domain = domainDao.findById(domainId);
|
||||
return domain != null ? domain.getName() : "";
|
||||
}).filter(name -> !name.isEmpty()).reduce((a, b) -> a + "," + b).orElse("");
|
||||
response.setDomain(domainNames);
|
||||
response.setDomainId(domainUUIDs);
|
||||
}
|
||||
if (crossZoneInstanceCreation) {
|
||||
response.setCrossZoneInstanceCreation(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
package org.apache.cloudstack.backup.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.backup.BackupOfferingDetailsVO;
|
||||
import org.apache.cloudstack.resourcedetail.ResourceDetailsDao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface BackupOfferingDetailsDao extends GenericDao<BackupOfferingDetailsVO, Long>, ResourceDetailsDao<BackupOfferingDetailsVO> {
|
||||
List<Long> findDomainIds(final long resourceId);
|
||||
List<Long> findZoneIds(final long resourceId);
|
||||
String getDetail(Long backupOfferingId, String key);
|
||||
List<Long> findOfferingIdsByDomainIds(List<Long> domainIds);
|
||||
void updateBackupOfferingDomainIdsDetail(long backupOfferingId, List<Long> filteredDomainIds);
|
||||
}
|
||||
|
|
@ -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.backup.dao;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.backup.BackupOfferingDetailsVO;
|
||||
import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class BackupOfferingDetailsDaoImpl extends ResourceDetailsDaoBase<BackupOfferingDetailsVO> implements BackupOfferingDetailsDao {
|
||||
|
||||
@Override
|
||||
public void addDetail(long resourceId, String key, String value, boolean display) {
|
||||
super.addDetail(new BackupOfferingDetailsVO(resourceId, key, value, display));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> findDomainIds(long resourceId) {
|
||||
final List<Long> domainIds = new ArrayList<>();
|
||||
for (final BackupOfferingDetailsVO detail: findDetails(resourceId, ApiConstants.DOMAIN_ID)) {
|
||||
final Long domainId = Long.valueOf(detail.getValue());
|
||||
if (domainId > 0) {
|
||||
domainIds.add(domainId);
|
||||
}
|
||||
}
|
||||
return domainIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> findZoneIds(long resourceId) {
|
||||
final List<Long> zoneIds = new ArrayList<>();
|
||||
for (final BackupOfferingDetailsVO detail: findDetails(resourceId, ApiConstants.ZONE_ID)) {
|
||||
final Long zoneId = Long.valueOf(detail.getValue());
|
||||
if (zoneId > 0) {
|
||||
zoneIds.add(zoneId);
|
||||
}
|
||||
}
|
||||
return zoneIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetail(Long backupOfferingId, String key) {
|
||||
String detailValue = null;
|
||||
BackupOfferingDetailsVO backupOfferingDetail = findDetail(backupOfferingId, key);
|
||||
if (backupOfferingDetail != null) {
|
||||
detailValue = backupOfferingDetail.getValue();
|
||||
}
|
||||
return detailValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> findOfferingIdsByDomainIds(List<Long> domainIds) {
|
||||
Object[] dIds = domainIds.stream().map(s -> String.valueOf(s)).collect(Collectors.toList()).toArray();
|
||||
return findResourceIdsByNameAndValueIn("domainid", dIds);
|
||||
}
|
||||
|
||||
@DB
|
||||
@Override
|
||||
public void updateBackupOfferingDomainIdsDetail(long backupOfferingId, List<Long> filteredDomainIds) {
|
||||
SearchBuilder<BackupOfferingDetailsVO> sb = createSearchBuilder();
|
||||
List<BackupOfferingDetailsVO> detailsVO = new ArrayList<>();
|
||||
sb.and("offeringId", sb.entity().getResourceId(), SearchCriteria.Op.EQ);
|
||||
sb.and("detailName", sb.entity().getName(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
SearchCriteria<BackupOfferingDetailsVO> sc = sb.create();
|
||||
sc.setParameters("offeringId", String.valueOf(backupOfferingId));
|
||||
sc.setParameters("detailName", ApiConstants.DOMAIN_ID);
|
||||
remove(sc);
|
||||
for (Long domainId : filteredDomainIds) {
|
||||
detailsVO.add(new BackupOfferingDetailsVO(backupOfferingId, ApiConstants.DOMAIN_ID, String.valueOf(domainId), false));
|
||||
}
|
||||
if (!detailsVO.isEmpty()) {
|
||||
for (BackupOfferingDetailsVO detailVO : detailsVO) {
|
||||
persist(detailVO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -76,6 +76,8 @@ StateDao<ObjectInDataStoreStateMachine.State, ObjectInDataStoreStateMachine.Even
|
|||
|
||||
List<SnapshotDataStoreVO> findBySnapshotId(long snapshotId);
|
||||
|
||||
List<SnapshotDataStoreVO> findBySnapshotIdWithNonDestroyedState(long snapshotId);
|
||||
|
||||
void duplicateCacheRecordsOnRegionStore(long storeId);
|
||||
|
||||
// delete the snapshot entry on primary data store to make sure that next snapshot will be full snapshot
|
||||
|
|
|
|||
|
|
@ -464,6 +464,13 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
|||
|
||||
@Override
|
||||
public List<SnapshotDataStoreVO> findBySnapshotId(long snapshotId) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create();
|
||||
sc.setParameters(SNAPSHOT_ID, snapshotId);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotDataStoreVO> findBySnapshotIdWithNonDestroyedState(long snapshotId) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = idStateNinSearch.create();
|
||||
sc.setParameters(SNAPSHOT_ID, snapshotId);
|
||||
sc.setParameters(STATE, State.Destroyed.name());
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@
|
|||
<bean id="NetworkDaoImpl" class="org.apache.cloudstack.quota.dao.NetworkDaoImpl" />
|
||||
<bean id="VpcDaoImpl" class="org.apache.cloudstack.quota.dao.VpcDaoImpl" />
|
||||
<bean id="volumeDaoImpl" class="com.cloud.storage.dao.VolumeDaoImpl" />
|
||||
<bean id="reservationDao" class="org.apache.cloudstack.reservation.dao.ReservationDaoImpl" />
|
||||
<bean id="reservationDao" class="org.apache.cloudstack.reservation.dao.ReservationDaoImpl" />
|
||||
<bean id="backupOfferingDaoImpl" class="org.apache.cloudstack.backup.dao.BackupOfferingDaoImpl" />
|
||||
<bean id="backupOfferingDetailsDaoImpl" class="org.apache.cloudstack.backup.dao.BackupOfferingDetailsDaoImpl" />
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
-- 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.
|
||||
|
||||
-- in cloud
|
||||
DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_DROP_COLUMN`;
|
||||
|
||||
-- Error 1091: Can't DROP column; check that column/key exists
|
||||
CREATE PROCEDURE `cloud`.`IDEMPOTENT_DROP_COLUMN` (
|
||||
IN in_table_name VARCHAR(200),
|
||||
IN in_column_name VARCHAR(200)
|
||||
)
|
||||
BEGIN
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR 1091 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name, ' DROP COLUMN ', in_column_name); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END;
|
||||
|
|
@ -301,7 +301,7 @@ CALL `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY`('cloud.service_offering','fk_service_
|
|||
CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.service_offering', 'fk_service_offering__vgpu_profile_id', '(vgpu_profile_id)', '`vgpu_profile`(`id`)');
|
||||
|
||||
-- Netris Plugin
|
||||
CREATE TABLE `cloud`.`netris_providers` (
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`netris_providers` (
|
||||
`id` bigint unsigned NOT NULL auto_increment COMMENT 'id',
|
||||
`uuid` varchar(40),
|
||||
`zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID',
|
||||
|
|
@ -321,11 +321,11 @@ CREATE TABLE `cloud`.`netris_providers` (
|
|||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Drop the Tungsten and NSX columns from the network offerings (replaced by checking the provider on the ntwk_offering_service_map table)
|
||||
ALTER TABLE `cloud`.`network_offerings` DROP COLUMN `for_tungsten`;
|
||||
ALTER TABLE `cloud`.`network_offerings` DROP COLUMN `for_nsx`;
|
||||
CALL `cloud`.`IDEMPOTENT_DROP_COLUMN`('cloud.network_offerings', 'for_tungsten');
|
||||
CALL `cloud`.`IDEMPOTENT_DROP_COLUMN`('cloud.network_offerings', 'for_nsx');
|
||||
|
||||
-- Drop the Tungsten and NSX columns from the VPC offerings (replaced by checking the provider on the vpc_offering_service_map table)
|
||||
ALTER TABLE `cloud`.`vpc_offerings` DROP COLUMN `for_nsx`;
|
||||
CALL `cloud`.`IDEMPOTENT_DROP_COLUMN`('cloud.vpc_offerings', 'for_nsx');
|
||||
|
||||
-- Add next_hop to the static_routes table
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.static_routes', 'next_hop', 'varchar(50) COMMENT "next hop of the static route" AFTER `vpc_gateway_id`');
|
||||
|
|
|
|||
|
|
@ -19,7 +19,33 @@
|
|||
-- Schema upgrade from 4.22.1.0 to 4.23.0.0
|
||||
--;
|
||||
|
||||
CREATE TABLE `cloud`.`backup_offering_details` (
|
||||
`id` bigint unsigned NOT NULL auto_increment,
|
||||
`backup_offering_id` bigint unsigned NOT NULL COMMENT 'Backup offering id',
|
||||
`name` varchar(255) NOT NULL,
|
||||
`value` varchar(1024) NOT NULL,
|
||||
`display` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should detail be displayed to the end user',
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `fk_offering_details__backup_offering_id` FOREIGN KEY `fk_offering_details__backup_offering_id`(`backup_offering_id`) REFERENCES `backup_offering`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Update value to random for the config 'vm.allocation.algorithm' or 'volume.allocation.algorithm' if configured as userconcentratedpod_random
|
||||
-- Update value to firstfit for the config 'vm.allocation.algorithm' or 'volume.allocation.algorithm' if configured as userconcentratedpod_firstfit
|
||||
UPDATE `cloud`.`configuration` SET value='random' WHERE name IN ('vm.allocation.algorithm', 'volume.allocation.algorithm') AND value='userconcentratedpod_random';
|
||||
UPDATE `cloud`.`configuration` SET value='firstfit' WHERE name IN ('vm.allocation.algorithm', 'volume.allocation.algorithm') AND value='userconcentratedpod_firstfit';
|
||||
|
||||
-- Create webhook_filter table
|
||||
DROP TABLE IF EXISTS `cloud`.`webhook_filter`;
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`webhook_filter` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the webhook filter',
|
||||
`uuid` varchar(255) COMMENT 'uuid of the webhook filter',
|
||||
`webhook_id` bigint unsigned NOT NULL COMMENT 'id of the webhook',
|
||||
`type` varchar(20) COMMENT 'type of the filter',
|
||||
`mode` varchar(20) COMMENT 'mode of the filter',
|
||||
`match_type` varchar(20) COMMENT 'match type of the filter',
|
||||
`value` varchar(256) NOT NULL COMMENT 'value of the filter used for matching',
|
||||
`created` datetime NOT NULL COMMENT 'date created',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `i_webhook_filter__webhook_id`(`webhook_id`),
|
||||
CONSTRAINT `fk_webhook_filter__webhook_id` FOREIGN KEY(`webhook_id`) REFERENCES `webhook`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
|
|
|||
|
|
@ -76,7 +76,8 @@ public class VMTemplateDaoImplTest {
|
|||
VMTemplateVO expectedTemplate = new VMTemplateVO();
|
||||
List<VMTemplateVO> returnedList = Collections.singletonList(expectedTemplate);
|
||||
doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
|
||||
VMTemplateVO result = templateDao.findLatestTemplateByName("test", CPU.CPUArch.getDefault());
|
||||
VMTemplateVO result = templateDao.findLatestTemplateByName("test", Hypervisor.HypervisorType.KVM,
|
||||
CPU.CPUArch.getDefault());
|
||||
assertNotNull("Expected a non-null template", result);
|
||||
assertEquals("Expected the returned template to be the first element", expectedTemplate, result);
|
||||
}
|
||||
|
|
@ -85,7 +86,8 @@ public class VMTemplateDaoImplTest {
|
|||
public void testFindLatestTemplateByName_ReturnsNullWhenNoTemplateFound() {
|
||||
List<VMTemplateVO> emptyList = Collections.emptyList();
|
||||
doReturn(emptyList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
|
||||
VMTemplateVO result = templateDao.findLatestTemplateByName("test", CPU.CPUArch.getDefault());
|
||||
VMTemplateVO result = templateDao.findLatestTemplateByName("test", Hypervisor.HypervisorType.VMware,
|
||||
CPU.CPUArch.getDefault());
|
||||
assertNull("Expected null when no templates are found", result);
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +96,8 @@ public class VMTemplateDaoImplTest {
|
|||
VMTemplateVO expectedTemplate = new VMTemplateVO();
|
||||
List<VMTemplateVO> returnedList = Collections.singletonList(expectedTemplate);
|
||||
doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
|
||||
VMTemplateVO result = templateDao.findLatestTemplateByName("test", null);
|
||||
VMTemplateVO result = templateDao.findLatestTemplateByName("test", Hypervisor.HypervisorType.XenServer,
|
||||
null);
|
||||
assertNotNull("Expected a non-null template even if arch is null", result);
|
||||
assertEquals("Expected the returned template to be the first element", expectedTemplate, result);
|
||||
}
|
||||
|
|
@ -337,4 +340,82 @@ public class VMTemplateDaoImplTest {
|
|||
VMTemplateVO readyTemplate = templateDao.findSystemVMReadyTemplate(zoneId, Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64.getType());
|
||||
Assert.assertEquals(CPU.CPUArch.arm64, readyTemplate.getArch());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findActiveSystemTemplateByHypervisorArchAndUrlPath_ReturnsTemplate() {
|
||||
VMTemplateVO expectedTemplate = mock(VMTemplateVO.class);
|
||||
SearchBuilder<VMTemplateVO> sb = mock(SearchBuilder.class);
|
||||
when(sb.entity()).thenReturn(expectedTemplate);
|
||||
SearchCriteria<VMTemplateVO>sc = mock(SearchCriteria.class);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(templateDao.createSearchBuilder()).thenReturn(sb);
|
||||
List<VMTemplateVO> templates = Collections.singletonList(expectedTemplate);
|
||||
doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
|
||||
|
||||
VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath(
|
||||
Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, "testPath");
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(expectedTemplate, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findActiveSystemTemplateByHypervisorArchAndUrlPath_ReturnsNullWhenNoTemplatesFound() {
|
||||
VMTemplateVO template = mock(VMTemplateVO.class);
|
||||
SearchBuilder<VMTemplateVO> sb = mock(SearchBuilder.class);
|
||||
when(sb.entity()).thenReturn(template);
|
||||
SearchCriteria<VMTemplateVO>sc = mock(SearchCriteria.class);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(templateDao.createSearchBuilder()).thenReturn(sb);
|
||||
doReturn(Collections.emptyList()).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
|
||||
|
||||
VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath(
|
||||
Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, "testPath");
|
||||
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findActiveSystemTemplateByHypervisorArchAndUrlPath_NullHypervisor() {
|
||||
VMTemplateVO expectedTemplate = mock(VMTemplateVO.class);
|
||||
SearchBuilder<VMTemplateVO> sb = mock(SearchBuilder.class);
|
||||
when(sb.entity()).thenReturn(expectedTemplate);
|
||||
SearchCriteria<VMTemplateVO>sc = mock(SearchCriteria.class);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(templateDao.createSearchBuilder()).thenReturn(sb);
|
||||
List<VMTemplateVO> templates = Collections.singletonList(expectedTemplate);
|
||||
doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
|
||||
|
||||
VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath(
|
||||
null, CPU.CPUArch.amd64, "testPath");
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(expectedTemplate, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findActiveSystemTemplateByHypervisorArchAndUrlPath_NullArch() {
|
||||
VMTemplateVO expectedTemplate = mock(VMTemplateVO.class);
|
||||
SearchBuilder<VMTemplateVO> sb = mock(SearchBuilder.class);
|
||||
when(sb.entity()).thenReturn(expectedTemplate);
|
||||
SearchCriteria<VMTemplateVO>sc = mock(SearchCriteria.class);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(templateDao.createSearchBuilder()).thenReturn(sb);
|
||||
List<VMTemplateVO> templates = Collections.singletonList(expectedTemplate);
|
||||
doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
|
||||
|
||||
VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath(
|
||||
Hypervisor.HypervisorType.KVM, null, "testPath");
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(expectedTemplate, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findActiveSystemTemplateByHypervisorArchAndUrlPath_EmptyUrlPathSuffix() {
|
||||
VMTemplateVO result = templateDao.findActiveSystemTemplateByHypervisorArchAndUrlPath(
|
||||
Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64, "");
|
||||
|
||||
assertNull(result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,251 @@
|
|||
// 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.backup.dao;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.backup.BackupOfferingDetailsVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class BackupOfferingDetailsDaoImplTest {
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
private BackupOfferingDetailsDaoImpl backupOfferingDetailsDao;
|
||||
|
||||
private static final long RESOURCE_ID = 1L;
|
||||
private static final long OFFERING_ID = 100L;
|
||||
private static final String TEST_KEY = "testKey";
|
||||
private static final String TEST_VALUE = "testValue";
|
||||
|
||||
@Test
|
||||
public void testAddDetail() {
|
||||
BackupOfferingDetailsVO detailVO = new BackupOfferingDetailsVO(RESOURCE_ID, TEST_KEY, TEST_VALUE, true);
|
||||
|
||||
Assert.assertEquals("Resource ID should match", RESOURCE_ID, detailVO.getResourceId());
|
||||
Assert.assertEquals("Detail name/key should match", TEST_KEY, detailVO.getName());
|
||||
Assert.assertEquals("Detail value should match", TEST_VALUE, detailVO.getValue());
|
||||
Assert.assertTrue("Display flag should be true", detailVO.isDisplay());
|
||||
|
||||
BackupOfferingDetailsVO detailVOHidden = new BackupOfferingDetailsVO(RESOURCE_ID, "hiddenKey", "hiddenValue", false);
|
||||
Assert.assertFalse("Display flag should be false", detailVOHidden.isDisplay());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindDomainIdsWithMultipleDomains() {
|
||||
List<BackupOfferingDetailsVO> mockDetails = Arrays.asList(
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.DOMAIN_ID, "1", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.DOMAIN_ID, "2", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.DOMAIN_ID, "3", false)
|
||||
);
|
||||
|
||||
Mockito.doReturn(mockDetails).when(backupOfferingDetailsDao)
|
||||
.findDetails(RESOURCE_ID, ApiConstants.DOMAIN_ID);
|
||||
|
||||
List<Long> domainIds = backupOfferingDetailsDao.findDomainIds(RESOURCE_ID);
|
||||
|
||||
Assert.assertNotNull(domainIds);
|
||||
Assert.assertEquals(3, domainIds.size());
|
||||
Assert.assertEquals(Arrays.asList(1L, 2L, 3L), domainIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindDomainIdsWithEmptyList() {
|
||||
Mockito.doReturn(Collections.emptyList()).when(backupOfferingDetailsDao)
|
||||
.findDetails(RESOURCE_ID, ApiConstants.DOMAIN_ID);
|
||||
|
||||
List<Long> domainIds = backupOfferingDetailsDao.findDomainIds(RESOURCE_ID);
|
||||
|
||||
Assert.assertNotNull(domainIds);
|
||||
Assert.assertTrue(domainIds.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindDomainIdsExcludesZeroOrNegativeValues() {
|
||||
List<BackupOfferingDetailsVO> mockDetails = Arrays.asList(
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.DOMAIN_ID, "1", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.DOMAIN_ID, "0", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.DOMAIN_ID, "-1", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.DOMAIN_ID, "2", false)
|
||||
);
|
||||
|
||||
Mockito.doReturn(mockDetails).when(backupOfferingDetailsDao)
|
||||
.findDetails(RESOURCE_ID, ApiConstants.DOMAIN_ID);
|
||||
|
||||
List<Long> domainIds = backupOfferingDetailsDao.findDomainIds(RESOURCE_ID);
|
||||
|
||||
Assert.assertNotNull(domainIds);
|
||||
Assert.assertEquals(2, domainIds.size());
|
||||
Assert.assertEquals(Arrays.asList(1L, 2L), domainIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindZoneIdsWithMultipleZones() {
|
||||
List<BackupOfferingDetailsVO> mockDetails = Arrays.asList(
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.ZONE_ID, "10", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.ZONE_ID, "20", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.ZONE_ID, "30", false)
|
||||
);
|
||||
|
||||
Mockito.doReturn(mockDetails).when(backupOfferingDetailsDao)
|
||||
.findDetails(RESOURCE_ID, ApiConstants.ZONE_ID);
|
||||
|
||||
List<Long> zoneIds = backupOfferingDetailsDao.findZoneIds(RESOURCE_ID);
|
||||
|
||||
Assert.assertNotNull(zoneIds);
|
||||
Assert.assertEquals(3, zoneIds.size());
|
||||
Assert.assertEquals(Arrays.asList(10L, 20L, 30L), zoneIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindZoneIdsWithEmptyList() {
|
||||
Mockito.doReturn(Collections.emptyList()).when(backupOfferingDetailsDao)
|
||||
.findDetails(RESOURCE_ID, ApiConstants.ZONE_ID);
|
||||
|
||||
List<Long> zoneIds = backupOfferingDetailsDao.findZoneIds(RESOURCE_ID);
|
||||
|
||||
Assert.assertNotNull(zoneIds);
|
||||
Assert.assertTrue(zoneIds.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindZoneIdsExcludesZeroOrNegativeValues() {
|
||||
List<BackupOfferingDetailsVO> mockDetails = Arrays.asList(
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.ZONE_ID, "10", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.ZONE_ID, "0", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.ZONE_ID, "-5", false),
|
||||
createDetailVO(RESOURCE_ID, ApiConstants.ZONE_ID, "20", false)
|
||||
);
|
||||
|
||||
Mockito.doReturn(mockDetails).when(backupOfferingDetailsDao)
|
||||
.findDetails(RESOURCE_ID, ApiConstants.ZONE_ID);
|
||||
|
||||
List<Long> zoneIds = backupOfferingDetailsDao.findZoneIds(RESOURCE_ID);
|
||||
|
||||
Assert.assertNotNull(zoneIds);
|
||||
Assert.assertEquals(2, zoneIds.size());
|
||||
Assert.assertEquals(Arrays.asList(10L, 20L), zoneIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDetailWhenDetailExists() {
|
||||
BackupOfferingDetailsVO mockDetail = createDetailVO(OFFERING_ID, TEST_KEY, TEST_VALUE, true);
|
||||
|
||||
Mockito.doReturn(mockDetail).when(backupOfferingDetailsDao)
|
||||
.findDetail(OFFERING_ID, TEST_KEY);
|
||||
|
||||
String detailValue = backupOfferingDetailsDao.getDetail(OFFERING_ID, TEST_KEY);
|
||||
|
||||
Assert.assertNotNull(detailValue);
|
||||
Assert.assertEquals(TEST_VALUE, detailValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDetailWhenDetailDoesNotExist() {
|
||||
Mockito.doReturn(null).when(backupOfferingDetailsDao)
|
||||
.findDetail(OFFERING_ID, TEST_KEY);
|
||||
|
||||
String detailValue = backupOfferingDetailsDao.getDetail(OFFERING_ID, TEST_KEY);
|
||||
|
||||
Assert.assertNull(detailValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindOfferingIdsByDomainIds() {
|
||||
List<Long> domainIds = Arrays.asList(1L, 2L, 3L);
|
||||
List<Long> expectedOfferingIds = Arrays.asList(100L, 101L, 102L);
|
||||
|
||||
Mockito.doReturn(expectedOfferingIds).when(backupOfferingDetailsDao)
|
||||
.findResourceIdsByNameAndValueIn(Mockito.eq("domainid"), Mockito.any(Object[].class));
|
||||
|
||||
List<Long> offeringIds = backupOfferingDetailsDao.findOfferingIdsByDomainIds(domainIds);
|
||||
|
||||
Assert.assertNotNull(offeringIds);
|
||||
Assert.assertEquals(expectedOfferingIds, offeringIds);
|
||||
Mockito.verify(backupOfferingDetailsDao).findResourceIdsByNameAndValueIn(
|
||||
Mockito.eq("domainid"), Mockito.any(Object[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindOfferingIdsByDomainIdsWithEmptyList() {
|
||||
List<Long> domainIds = Collections.emptyList();
|
||||
List<Long> expectedOfferingIds = Collections.emptyList();
|
||||
|
||||
Mockito.doReturn(expectedOfferingIds).when(backupOfferingDetailsDao)
|
||||
.findResourceIdsByNameAndValueIn(Mockito.eq("domainid"), Mockito.any(Object[].class));
|
||||
|
||||
List<Long> offeringIds = backupOfferingDetailsDao.findOfferingIdsByDomainIds(domainIds);
|
||||
|
||||
Assert.assertNotNull(offeringIds);
|
||||
Assert.assertTrue(offeringIds.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testUpdateBackupOfferingDomainIdsDetail() {
|
||||
List<Long> newDomainIds = Arrays.asList(1L, 2L, 3L);
|
||||
|
||||
Mockito.doReturn(0).when(backupOfferingDetailsDao).remove(Mockito.any(SearchCriteria.class));
|
||||
Mockito.doReturn(null).when(backupOfferingDetailsDao).persist(Mockito.any(BackupOfferingDetailsVO.class));
|
||||
|
||||
backupOfferingDetailsDao.updateBackupOfferingDomainIdsDetail(OFFERING_ID, newDomainIds);
|
||||
|
||||
Mockito.verify(backupOfferingDetailsDao, Mockito.times(3)).persist(Mockito.any(BackupOfferingDetailsVO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testUpdateBackupOfferingDomainIdsDetailWithEmptyList() {
|
||||
List<Long> emptyDomainIds = Collections.emptyList();
|
||||
|
||||
Mockito.doReturn(0).when(backupOfferingDetailsDao).remove(Mockito.any(SearchCriteria.class));
|
||||
|
||||
backupOfferingDetailsDao.updateBackupOfferingDomainIdsDetail(OFFERING_ID, emptyDomainIds);
|
||||
|
||||
Mockito.verify(backupOfferingDetailsDao, Mockito.never()).persist(Mockito.any(BackupOfferingDetailsVO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testUpdateBackupOfferingDomainIdsDetailWithSingleDomain() {
|
||||
List<Long> singleDomainId = Collections.singletonList(5L);
|
||||
|
||||
Mockito.doReturn(0).when(backupOfferingDetailsDao).remove(Mockito.any(SearchCriteria.class));
|
||||
Mockito.doReturn(null).when(backupOfferingDetailsDao).persist(Mockito.any(BackupOfferingDetailsVO.class));
|
||||
|
||||
backupOfferingDetailsDao.updateBackupOfferingDomainIdsDetail(OFFERING_ID, singleDomainId);
|
||||
|
||||
Mockito.verify(backupOfferingDetailsDao, Mockito.times(1)).persist(Mockito.any(BackupOfferingDetailsVO.class));
|
||||
}
|
||||
|
||||
private BackupOfferingDetailsVO createDetailVO(long resourceId, String name, String value, boolean display) {
|
||||
return new BackupOfferingDetailsVO(resourceId, name, value, display);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ function getTemplateVersion() {
|
|||
export CS_VERSION="${subversion1}"."${subversion2}"
|
||||
export CS_MINOR_VERSION="${minorversion}"
|
||||
export VERSION="${CS_VERSION}.${CS_MINOR_VERSION}"
|
||||
export CS_SYSTEMTEMPLATE_REPO="https://download.cloudstack.org/systemvm/"
|
||||
}
|
||||
|
||||
function getGenericName() {
|
||||
|
|
@ -63,7 +64,7 @@ function getChecksum() {
|
|||
|
||||
function createMetadataFile() {
|
||||
local fileData=$(cat "$SOURCEFILE")
|
||||
echo -e "["default"]\nversion = $VERSION.${securityversion}\n" >> "$METADATAFILE"
|
||||
echo -e "["default"]\nversion = $VERSION.${securityversion}\ndownloadrepository = $CS_SYSTEMTEMPLATE_REPO\n" >> "$METADATAFILE"
|
||||
for template in "${templates[@]}"
|
||||
do
|
||||
section="${template%%:*}"
|
||||
|
|
@ -82,13 +83,21 @@ function createMetadataFile() {
|
|||
|
||||
declare -a templates
|
||||
getTemplateVersion $1
|
||||
templates=( "kvm-x86_64:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-x86_64-kvm.qcow2.bz2"
|
||||
"kvm-aarch64:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-aarch64-kvm.qcow2.bz2"
|
||||
"vmware:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-x86_64-vmware.ova"
|
||||
"xenserver:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-xen.vhd.bz2"
|
||||
"hyperv:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-hyperv.vhd.zip"
|
||||
"lxc:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-kvm.qcow2.bz2"
|
||||
"ovm3:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-ovm.raw.bz2" )
|
||||
declare -A template_specs=(
|
||||
[kvm-x86_64]="x86_64-kvm.qcow2.bz2"
|
||||
[kvm-aarch64]="aarch64-kvm.qcow2.bz2"
|
||||
[vmware]="x86_64-vmware.ova"
|
||||
[xenserver]="x86_64-xen.vhd.bz2"
|
||||
[hyperv]="x86_64-hyperv.vhd.zip"
|
||||
[lxc]="x86_64-kvm.qcow2.bz2"
|
||||
[ovm3]="x86_64-ovm.raw.bz2"
|
||||
)
|
||||
|
||||
templates=()
|
||||
for key in "${!template_specs[@]}"; do
|
||||
url="${CS_SYSTEMTEMPLATE_REPO}/${CS_VERSION}/systemvmtemplate-$VERSION-${template_specs[$key]}"
|
||||
templates+=("$key:$url")
|
||||
done
|
||||
|
||||
PARENTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )/dist/systemvm-templates/"
|
||||
mkdir -p "$PARENTPATH"
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ public class StorageCacheManagerImpl implements StorageCacheManager, Manager {
|
|||
if (result.isFailed()) {
|
||||
objOnCacheStore.processEvent(Event.OperationFailed);
|
||||
} else {
|
||||
objOnCacheStore.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
objOnCacheStore.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
objOnCacheStore.incRefCount();
|
||||
return objOnCacheStore;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
|
|||
return answer;
|
||||
}
|
||||
|
||||
objOnImageStore.processEvent(Event.OperationSuccessed, answer);
|
||||
objOnImageStore.processEvent(Event.OperationSucceeded, answer);
|
||||
|
||||
objOnImageStore.processEvent(Event.CopyingRequested);
|
||||
|
||||
|
|
@ -432,7 +432,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
|
|||
throw e;
|
||||
}
|
||||
|
||||
objOnImageStore.processEvent(Event.OperationSuccessed);
|
||||
objOnImageStore.processEvent(Event.OperationSucceeded);
|
||||
deleteVolumeOnSecondaryStore(objOnImageStore);
|
||||
return answer;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1058,7 +1058,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
|||
|
||||
//submit processEvent
|
||||
if (StringUtils.isEmpty(errMsg)) {
|
||||
snapshotInfo.processEvent(Event.OperationSuccessed);
|
||||
snapshotInfo.processEvent(Event.OperationSucceeded);
|
||||
} else {
|
||||
snapshotInfo.processEvent(Event.OperationFailed);
|
||||
}
|
||||
|
|
@ -1221,7 +1221,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
|||
// command to copy this data from cache to secondary storage. We
|
||||
// then clean up the cache.
|
||||
|
||||
destOnStore.processEvent(Event.OperationSuccessed, copyCmdAnswer);
|
||||
destOnStore.processEvent(Event.OperationSucceeded, copyCmdAnswer);
|
||||
|
||||
CopyCommand cmd = new CopyCommand(destOnStore.getTO(), destData.getTO(), primaryStorageDownloadWait,
|
||||
VirtualMachineManager.ExecuteInSequence.value());
|
||||
|
|
@ -1271,7 +1271,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
|||
|
||||
try {
|
||||
if (StringUtils.isEmpty(errMsg)) {
|
||||
snapshotInfo.processEvent(Event.OperationSuccessed);
|
||||
snapshotInfo.processEvent(Event.OperationSucceeded);
|
||||
}
|
||||
else {
|
||||
snapshotInfo.processEvent(Event.OperationFailed);
|
||||
|
|
@ -1404,7 +1404,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
|||
|
||||
try {
|
||||
if (StringUtils.isEmpty(errMsg)) {
|
||||
snapshotInfo.processEvent(Event.OperationSuccessed);
|
||||
snapshotInfo.processEvent(Event.OperationSucceeded);
|
||||
}
|
||||
else {
|
||||
snapshotInfo.processEvent(Event.OperationFailed);
|
||||
|
|
@ -2366,7 +2366,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
|||
|
||||
_volumeDao.update(volumeVO.getId(), volumeVO);
|
||||
|
||||
_volumeService.copyPoliciesBetweenVolumesAndDestroySourceVolumeAfterMigration(Event.OperationSuccessed, null, srcVolumeInfo, destVolumeInfo, false);
|
||||
_volumeService.copyPoliciesBetweenVolumesAndDestroySourceVolumeAfterMigration(Event.OperationSucceeded, null, srcVolumeInfo, destVolumeInfo, false);
|
||||
|
||||
// Update the volume ID for snapshots on secondary storage
|
||||
if (!_snapshotDao.listByVolumeId(srcVolumeInfo.getId()).isEmpty()) {
|
||||
|
|
@ -2704,7 +2704,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
|||
|
||||
try {
|
||||
if (StringUtils.isEmpty(errMsg)) {
|
||||
volumeInfo.processEvent(Event.OperationSuccessed);
|
||||
volumeInfo.processEvent(Event.OperationSucceeded);
|
||||
}
|
||||
else {
|
||||
volumeInfo.processEvent(Event.OperationFailed);
|
||||
|
|
|
|||
|
|
@ -257,9 +257,9 @@ public class SecondaryStorageServiceImpl implements SecondaryStorageService {
|
|||
}
|
||||
} else {
|
||||
if (destData instanceof VolumeInfo) {
|
||||
((VolumeInfo) destData).processEventOnly(ObjectInDataStoreStateMachine.Event.OperationSuccessed, answer);
|
||||
((VolumeInfo) destData).processEventOnly(ObjectInDataStoreStateMachine.Event.OperationSucceeded, answer);
|
||||
} else {
|
||||
destData.processEvent(ObjectInDataStoreStateMachine.Event.OperationSuccessed, answer);
|
||||
destData.processEvent(ObjectInDataStoreStateMachine.Event.OperationSucceeded, answer);
|
||||
}
|
||||
updateDataObject(srcData, destData);
|
||||
logger.debug("Deleting source data");
|
||||
|
|
|
|||
|
|
@ -671,7 +671,7 @@ public class TemplateServiceImpl implements TemplateService {
|
|||
TemplateApiResult res = new TemplateApiResult(tmplt);
|
||||
if (result.isSuccess()) {
|
||||
logger.info("Copied template [{}] to image store [{}].", tmplt.getUniqueName(), tmplt.getDataStore().getName());
|
||||
tmplt.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
tmplt.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
publishTemplateCreation(tmplt);
|
||||
} else {
|
||||
logger.warn("Failed to copy template [{}] to image store [{}].", tmplt.getUniqueName(), tmplt.getDataStore().getName());
|
||||
|
|
@ -824,7 +824,7 @@ public class TemplateServiceImpl implements TemplateService {
|
|||
}
|
||||
|
||||
try {
|
||||
template.processEvent(ObjectInDataStoreStateMachine.Event.OperationSuccessed);
|
||||
template.processEvent(ObjectInDataStoreStateMachine.Event.OperationSucceeded);
|
||||
} catch (Exception e) {
|
||||
result.setResult(e.toString());
|
||||
if (parentCallback != null) {
|
||||
|
|
@ -1033,7 +1033,7 @@ public class TemplateServiceImpl implements TemplateService {
|
|||
CommandResult result = callback.getResult();
|
||||
TemplateObject vo = context.getTemplate();
|
||||
if (result.isSuccess()) {
|
||||
vo.processEvent(Event.OperationSuccessed);
|
||||
vo.processEvent(Event.OperationSucceeded);
|
||||
} else {
|
||||
vo.processEvent(Event.OperationFailed);
|
||||
}
|
||||
|
|
@ -1093,7 +1093,7 @@ public class TemplateServiceImpl implements TemplateService {
|
|||
// no change to existing template_store_ref, will try to re-sync later if other call triggers this sync operation, like copy template
|
||||
} else {
|
||||
// this will update install path properly, next time it will not sync anymore.
|
||||
destTemplate.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
destTemplate.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
}
|
||||
future.complete(res);
|
||||
} catch (Exception e) {
|
||||
|
|
@ -1273,7 +1273,7 @@ public class TemplateServiceImpl implements TemplateService {
|
|||
res.setResult(result.getResult());
|
||||
destTemplate.processEvent(Event.OperationFailed);
|
||||
} else {
|
||||
destTemplate.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
destTemplate.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
}
|
||||
future.complete(res);
|
||||
} catch (Exception e) {
|
||||
|
|
@ -1298,7 +1298,7 @@ public class TemplateServiceImpl implements TemplateService {
|
|||
res.setResult(result.getResult());
|
||||
destTemplate.processEvent(Event.OperationFailed);
|
||||
} else {
|
||||
destTemplate.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
destTemplate.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
}
|
||||
future.complete(res);
|
||||
} catch (Exception e) {
|
||||
|
|
@ -1384,7 +1384,7 @@ public class TemplateServiceImpl implements TemplateService {
|
|||
TemplateApiResult dataDiskTemplateResult = new TemplateApiResult((TemplateObject)dataDiskTemplate);
|
||||
try {
|
||||
if (result.isSuccess()) {
|
||||
dataDiskTemplate.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
dataDiskTemplate.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
} else {
|
||||
dataDiskTemplate.processEvent(Event.OperationFailed);
|
||||
dataDiskTemplateResult.setResult(result.getResult());
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ public class SnapshotTest extends CloudStackTestNGBase {
|
|||
to.setSize(1000L);
|
||||
CopyCmdAnswer answer = new CopyCmdAnswer(to);
|
||||
templateOnStore.processEvent(Event.CreateOnlyRequested);
|
||||
templateOnStore.processEvent(Event.OperationSuccessed, answer);
|
||||
templateOnStore.processEvent(Event.OperationSucceeded, answer);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ public class VolumeTest extends CloudStackTestNGBase {
|
|||
to.setSize(100L);
|
||||
CopyCmdAnswer answer = new CopyCmdAnswer(to);
|
||||
templateOnStore.processEvent(Event.CreateOnlyRequested);
|
||||
templateOnStore.processEvent(Event.OperationSuccessed, answer);
|
||||
templateOnStore.processEvent(Event.OperationSucceeded, answer);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ public class VolumeTestVmware extends CloudStackTestNGBase {
|
|||
to.setPath(this.getImageInstallPath());
|
||||
CopyCmdAnswer answer = new CopyCmdAnswer(to);
|
||||
templateOnStore.processEvent(Event.CreateOnlyRequested);
|
||||
templateOnStore.processEvent(Event.OperationSuccessed, answer);
|
||||
templateOnStore.processEvent(Event.OperationSucceeded, answer);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
|||
|
||||
CreateObjectAnswer createSnapshotAnswer = new CreateObjectAnswer(snapTO);
|
||||
|
||||
snapshotOnImageStore.processEvent(Event.OperationSuccessed, createSnapshotAnswer);
|
||||
snapshotOnImageStore.processEvent(Event.OperationSucceeded, createSnapshotAnswer);
|
||||
SnapshotObject snapObj = castSnapshotInfoToSnapshotObject(snapshot);
|
||||
try {
|
||||
snapObj.processEvent(Snapshot.Event.OperationNotPerformed);
|
||||
|
|
@ -303,7 +303,7 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
|||
}
|
||||
|
||||
if (Snapshot.State.Error.equals(snapshotVO.getState())) {
|
||||
List<SnapshotDataStoreVO> storeRefs = snapshotStoreDao.findBySnapshotId(snapshotId);
|
||||
List<SnapshotDataStoreVO> storeRefs = snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
|
||||
List<Long> deletedRefs = new ArrayList<>();
|
||||
for (SnapshotDataStoreVO ref : storeRefs) {
|
||||
boolean refZoneIdMatch = false;
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory {
|
|||
if (snapshot == null) { //snapshot may have been removed;
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<SnapshotDataStoreVO> allSnapshotsAndDataStore = snapshotStoreDao.findBySnapshotId(snapshotId);
|
||||
List<SnapshotDataStoreVO> allSnapshotsAndDataStore = snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
|
||||
if (CollectionUtils.isEmpty(allSnapshotsAndDataStore)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
|
@ -118,7 +118,23 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory {
|
|||
if (snapshot == null) {
|
||||
return null;
|
||||
}
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(role, storeId, snapshotId);
|
||||
return getSnapshotOnStore(snapshot, storeId, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotInfo getSnapshotIncludingRemoved(long snapshotId, long storeId, DataStoreRole role) {
|
||||
SnapshotVO snapshot = snapshotDao.findByIdIncludingRemoved(snapshotId);
|
||||
if (snapshot == null) {
|
||||
return null;
|
||||
}
|
||||
return getSnapshotOnStore(snapshot, storeId, role);
|
||||
}
|
||||
|
||||
private SnapshotInfo getSnapshotOnStore(SnapshotVO snapshot, long storeId, DataStoreRole role) {
|
||||
if (snapshot == null) {
|
||||
return null;
|
||||
}
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(role, storeId, snapshot.getId());
|
||||
if (snapshotStore == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -207,7 +223,7 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory {
|
|||
|
||||
@Override
|
||||
public void updateOperationFailed(long snapshotId) throws NoTransitionException {
|
||||
List<SnapshotDataStoreVO> snapshotStoreRefs = snapshotStoreDao.findBySnapshotId(snapshotId);
|
||||
List<SnapshotDataStoreVO> snapshotStoreRefs = snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
|
||||
for (SnapshotDataStoreVO snapshotStoreRef : snapshotStoreRefs) {
|
||||
SnapshotInfo snapshotInfo = getSnapshot(snapshotStoreRef.getSnapshotId(), snapshotStoreRef.getDataStoreId(), snapshotStoreRef.getRole());
|
||||
if (snapshotInfo != null) {
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
}
|
||||
|
||||
try {
|
||||
snapshot.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
snapshot.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
snapshot.processEvent(Snapshot.Event.OperationSucceeded);
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to create snapshot: ", e);
|
||||
|
|
@ -474,8 +474,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
if (res.isFailed()) {
|
||||
throw new CloudRuntimeException(res.getResult());
|
||||
}
|
||||
SnapshotInfo destSnapshot = res.getSnapshot();
|
||||
return destSnapshot;
|
||||
return res.getSnapshot();
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("failed copy snapshot", e);
|
||||
throw new CloudRuntimeException("Failed to copy snapshot", e);
|
||||
|
|
@ -483,7 +482,6 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
logger.debug("Failed to copy snapshot", e);
|
||||
throw new CloudRuntimeException("Failed to copy snapshot", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected Void copySnapshotAsyncCallback(AsyncCallbackDispatcher<SnapshotServiceImpl, CopyCommandResult> callback, CopySnapshotContext<CommandResult> context) {
|
||||
|
|
@ -515,7 +513,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
|
||||
try {
|
||||
CopyCmdAnswer copyCmdAnswer = (CopyCmdAnswer)result.getAnswer();
|
||||
destSnapshot.processEvent(Event.OperationSuccessed, copyCmdAnswer);
|
||||
destSnapshot.processEvent(Event.OperationSucceeded, copyCmdAnswer);
|
||||
srcSnapshot.processEvent(Snapshot.Event.OperationSucceeded);
|
||||
snapResult = new SnapshotResult(_snapshotFactory.getSnapshot(destSnapshot.getId(), destSnapshot.getDataStore()), copyCmdAnswer);
|
||||
future.complete(snapResult);
|
||||
|
|
@ -540,7 +538,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
}
|
||||
try {
|
||||
Answer answer = result.getAnswer();
|
||||
destSnapshot.processEvent(Event.OperationSuccessed);
|
||||
destSnapshot.processEvent(Event.OperationSucceeded);
|
||||
snapResult = new SnapshotResult(_snapshotFactory.getSnapshot(destSnapshot.getId(), destSnapshot.getDataStore()), answer);
|
||||
future.complete(snapResult);
|
||||
} catch (Exception e) {
|
||||
|
|
@ -571,7 +569,6 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
}
|
||||
|
||||
protected Void deleteSnapshotCallback(AsyncCallbackDispatcher<SnapshotServiceImpl, CommandResult> callback, DeleteSnapshotContext<CommandResult> context) {
|
||||
|
||||
CommandResult result = callback.getResult();
|
||||
AsyncCallFuture<SnapshotResult> future = context.future;
|
||||
SnapshotInfo snapshot = context.snapshot;
|
||||
|
|
@ -583,7 +580,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
res = new SnapshotResult(context.snapshot, null);
|
||||
res.setResult(result.getResult());
|
||||
} else {
|
||||
snapshot.processEvent(ObjectInDataStoreStateMachine.Event.OperationSuccessed);
|
||||
snapshot.processEvent(ObjectInDataStoreStateMachine.Event.OperationSucceeded);
|
||||
res = new SnapshotResult(context.snapshot, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -729,7 +726,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
|
||||
if (snapshot != null) {
|
||||
if (snapshot.getState() != Snapshot.State.BackedUp) {
|
||||
List<SnapshotDataStoreVO> snapshotDataStoreVOs = _snapshotStoreDao.findBySnapshotId(snapshotId);
|
||||
List<SnapshotDataStoreVO> snapshotDataStoreVOs = _snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
|
||||
for (SnapshotDataStoreVO snapshotDataStoreVO : snapshotDataStoreVOs) {
|
||||
logger.debug("Remove snapshot {}, status {} on snapshot_store_ref table with id: {}", snapshot, snapshotDataStoreVO.getState(), snapshotDataStoreVO.getId());
|
||||
|
||||
|
|
@ -803,7 +800,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
// no change to existing snapshot_store_ref, will try to re-sync later if other call triggers this sync operation
|
||||
} else {
|
||||
// this will update install path properly, next time it will not sync anymore.
|
||||
destSnapshot.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
destSnapshot.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
}
|
||||
future.complete(res);
|
||||
} catch (Exception e) {
|
||||
|
|
@ -833,8 +830,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
try {
|
||||
SnapshotObject srcSnapshot = (SnapshotObject)snapshot;
|
||||
srcSnapshot.processEvent(Event.DestroyRequested);
|
||||
srcSnapshot.processEvent(Event.OperationSuccessed);
|
||||
|
||||
srcSnapshot.processEvent(Event.OperationSucceeded);
|
||||
srcSnapshot.processEvent(Snapshot.Event.OperationFailed);
|
||||
|
||||
_snapshotDetailsDao.removeDetail(srcSnapshot.getId(), AsyncJob.Constants.MS_ID);
|
||||
|
|
@ -845,7 +841,6 @@ public class SnapshotServiceImpl implements SnapshotService {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -541,7 +541,6 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
|||
logger.warn("Failed to clean up snapshot '" + snapshot.getId() + "' on primary storage: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private VMSnapshot takeHypervisorSnapshot(VolumeInfo volumeInfo) {
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ public class DataObjectManagerImpl implements DataObjectManager {
|
|||
}
|
||||
|
||||
try {
|
||||
objectInDataStoreMgr.update(objInStrore, ObjectInDataStoreStateMachine.Event.OperationSuccessed);
|
||||
objectInDataStoreMgr.update(objInStrore, ObjectInDataStoreStateMachine.Event.OperationSucceeded);
|
||||
} catch (NoTransitionException e) {
|
||||
try {
|
||||
objectInDataStoreMgr.update(objInStrore, ObjectInDataStoreStateMachine.Event.OperationFailed);
|
||||
|
|
@ -267,7 +267,7 @@ public class DataObjectManagerImpl implements DataObjectManager {
|
|||
}
|
||||
|
||||
try {
|
||||
objectInDataStoreMgr.update(destObj, ObjectInDataStoreStateMachine.Event.OperationSuccessed);
|
||||
objectInDataStoreMgr.update(destObj, ObjectInDataStoreStateMachine.Event.OperationSucceeded);
|
||||
} catch (NoTransitionException e) {
|
||||
logger.debug("Failed to update copying state: ", e);
|
||||
try {
|
||||
|
|
@ -341,7 +341,7 @@ public class DataObjectManagerImpl implements DataObjectManager {
|
|||
|
||||
} else {
|
||||
try {
|
||||
objectInDataStoreMgr.update(destObj, Event.OperationSuccessed);
|
||||
objectInDataStoreMgr.update(destObj, Event.OperationSucceeded);
|
||||
} catch (NoTransitionException e) {
|
||||
logger.debug("delete failed", e);
|
||||
} catch (ConcurrentOperationException e) {
|
||||
|
|
@ -365,7 +365,7 @@ public class DataObjectManagerImpl implements DataObjectManager {
|
|||
event = ObjectInDataStoreStateMachine.Event.CreateRequested;
|
||||
objectInDataStoreMgr.update(objInStore, event);
|
||||
|
||||
objectInDataStoreMgr.update(objInStore, ObjectInDataStoreStateMachine.Event.OperationSuccessed);
|
||||
objectInDataStoreMgr.update(objInStore, ObjectInDataStoreStateMachine.Event.OperationSucceeded);
|
||||
} catch (NoTransitionException e) {
|
||||
logger.debug("Failed to update state", e);
|
||||
throw new CloudRuntimeException("Failed to update state", e);
|
||||
|
|
|
|||
|
|
@ -99,27 +99,30 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager {
|
|||
stateMachines.addTransition(State.Allocated, Event.CreateOnlyRequested, State.Creating);
|
||||
stateMachines.addTransition(State.Allocated, Event.DestroyRequested, State.Destroying);
|
||||
stateMachines.addTransition(State.Allocated, Event.OperationFailed, State.Failed);
|
||||
stateMachines.addTransition(State.Allocated, Event.OperationSuccessed, State.Ready);
|
||||
stateMachines.addTransition(State.Allocated, Event.OperationSucceeded, State.Ready);
|
||||
stateMachines.addTransition(State.Creating, Event.OperationFailed, State.Allocated);
|
||||
stateMachines.addTransition(State.Creating, Event.OperationSuccessed, State.Ready);
|
||||
stateMachines.addTransition(State.Creating, Event.OperationSucceeded, State.Ready);
|
||||
stateMachines.addTransition(State.Ready, Event.CopyingRequested, State.Copying);
|
||||
stateMachines.addTransition(State.Copying, Event.OperationSuccessed, State.Ready);
|
||||
stateMachines.addTransition(State.Copying, Event.OperationSucceeded, State.Ready);
|
||||
stateMachines.addTransition(State.Copying, Event.OperationFailed, State.Ready);
|
||||
stateMachines.addTransition(State.Ready, Event.DestroyRequested, State.Destroying);
|
||||
stateMachines.addTransition(State.Destroying, Event.DestroyRequested, State.Destroying);
|
||||
stateMachines.addTransition(State.Destroying, Event.OperationSuccessed, State.Destroyed);
|
||||
stateMachines.addTransition(State.Destroying, Event.OperationSucceeded, State.Destroyed);
|
||||
stateMachines.addTransition(State.Destroying, Event.OperationFailed, State.Destroying);
|
||||
stateMachines.addTransition(State.Destroyed, Event.DestroyRequested, State.Destroyed);
|
||||
stateMachines.addTransition(State.Destroyed, Event.OperationSucceeded, State.Destroyed);
|
||||
stateMachines.addTransition(State.Destroyed, Event.OperationFailed, State.Destroyed);
|
||||
stateMachines.addTransition(State.Failed, Event.DestroyRequested, State.Destroying);
|
||||
// TODO: further investigate why an extra event is sent when it is
|
||||
// already Ready for DownloadListener
|
||||
stateMachines.addTransition(State.Ready, Event.OperationSuccessed, State.Ready);
|
||||
stateMachines.addTransition(State.Ready, Event.OperationSucceeded, State.Ready);
|
||||
// State transitions for data object migration
|
||||
stateMachines.addTransition(State.Ready, Event.MigrateDataRequested, State.Migrating);
|
||||
stateMachines.addTransition(State.Ready, Event.CopyRequested, State.Copying);
|
||||
stateMachines.addTransition(State.Allocated, Event.MigrateDataRequested, State.Migrating);
|
||||
stateMachines.addTransition(State.Migrating, Event.MigrationFailed, State.Failed);
|
||||
stateMachines.addTransition(State.Migrating, Event.MigrationSucceeded, State.Destroyed);
|
||||
stateMachines.addTransition(State.Migrating, Event.OperationSuccessed, State.Ready);
|
||||
stateMachines.addTransition(State.Migrating, Event.OperationSucceeded, State.Ready);
|
||||
stateMachines.addTransition(State.Migrating, Event.OperationFailed, State.Ready);
|
||||
stateMachines.addTransition(State.Hidden, Event.DestroyRequested, State.Destroying);
|
||||
}
|
||||
|
|
@ -360,9 +363,7 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager {
|
|||
break;
|
||||
}
|
||||
} else if (data.getType() == DataObjectType.TEMPLATE && data.getDataStore().getRole() == DataStoreRole.Primary) {
|
||||
|
||||
result = this.stateMachines.transitTo(obj, event, null, templatePoolDao);
|
||||
|
||||
} else if (data.getType() == DataObjectType.SNAPSHOT && data.getDataStore().getRole() == DataStoreRole.Primary) {
|
||||
result = this.stateMachines.transitTo(obj, event, null, snapshotDataStoreDao);
|
||||
} else {
|
||||
|
|
@ -422,7 +423,5 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager {
|
|||
}
|
||||
|
||||
return vo;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -427,7 +427,7 @@ public class VolumeObject implements VolumeInfo {
|
|||
}
|
||||
mapOfEvents.put(ObjectInDataStoreStateMachine.Event.DestroyRequested, Volume.Event.DestroyRequested);
|
||||
mapOfEvents.put(ObjectInDataStoreStateMachine.Event.ExpungeRequested, Volume.Event.ExpungingRequested);
|
||||
mapOfEvents.put(ObjectInDataStoreStateMachine.Event.OperationSuccessed, Volume.Event.OperationSucceeded);
|
||||
mapOfEvents.put(ObjectInDataStoreStateMachine.Event.OperationSucceeded, Volume.Event.OperationSucceeded);
|
||||
mapOfEvents.put(ObjectInDataStoreStateMachine.Event.MigrationCopySucceeded, Volume.Event.MigrationCopySucceeded);
|
||||
mapOfEvents.put(ObjectInDataStoreStateMachine.Event.OperationFailed, Volume.Event.OperationFailed);
|
||||
mapOfEvents.put(ObjectInDataStoreStateMachine.Event.MigrationCopyFailed, Volume.Event.MigrationCopyFailed);
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
DataObject vo = context.getVolume();
|
||||
String errMsg = null;
|
||||
if (result.isSuccess()) {
|
||||
vo.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
vo.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
} else {
|
||||
vo.processEvent(Event.OperationFailed);
|
||||
errMsg = result.getResult();
|
||||
|
|
@ -485,7 +485,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
VolumeApiResult apiResult = new VolumeApiResult(vo);
|
||||
try {
|
||||
if (result.isSuccess()) {
|
||||
vo.processEvent(Event.OperationSuccessed);
|
||||
vo.processEvent(Event.OperationSucceeded);
|
||||
|
||||
if (vo.getPassphraseId() != null) {
|
||||
vo.deletePassphrase();
|
||||
|
|
@ -758,7 +758,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
|
||||
if (result.isSuccess()) {
|
||||
((TemplateObject)templateOnPrimaryStoreObj).setInstallPath(result.getPath());
|
||||
templateOnPrimaryStoreObj.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
templateOnPrimaryStoreObj.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
} else {
|
||||
templateOnPrimaryStoreObj.processEvent(Event.OperationFailed);
|
||||
}
|
||||
|
|
@ -778,7 +778,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
DataObject templateOnPrimaryStoreObj = context.destObj;
|
||||
|
||||
if (result.isSuccess()) {
|
||||
templateOnPrimaryStoreObj.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
templateOnPrimaryStoreObj.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
} else {
|
||||
templateOnPrimaryStoreObj.processEvent(Event.OperationFailed);
|
||||
}
|
||||
|
|
@ -802,7 +802,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
return null;
|
||||
}
|
||||
|
||||
templateOnPrimaryStoreObj.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
templateOnPrimaryStoreObj.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
createVolumeFromBaseImageAsync(context.volume, templateOnPrimaryStoreObj, context.dataStore, future);
|
||||
return null;
|
||||
}
|
||||
|
|
@ -853,7 +853,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
String deployAsIsConfiguration = context.deployAsIsConfiguration;
|
||||
|
||||
if (result.isSuccess()) {
|
||||
vo.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
vo.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
} else {
|
||||
|
||||
vo.processEvent(Event.OperationFailed);
|
||||
|
|
@ -908,7 +908,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
}
|
||||
|
||||
volDao.update(volume.getId(), volume);
|
||||
vo.processEvent(Event.OperationSuccessed);
|
||||
vo.processEvent(Event.OperationSucceeded);
|
||||
} else {
|
||||
volResult.setResult(result.getResult());
|
||||
|
||||
|
|
@ -972,7 +972,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
throw new CloudRuntimeException(String.format("Unable to create template %s on primary storage %s: %s", templateOnPrimary.getImage(), destPrimaryDataStore, errMesg));
|
||||
}
|
||||
|
||||
templateOnPrimary.processEvent(Event.OperationSuccessed);
|
||||
templateOnPrimary.processEvent(Event.OperationSucceeded);
|
||||
|
||||
} catch (Throwable e) {
|
||||
logger.debug("Failed to create template volume on storage", e);
|
||||
|
|
@ -1445,7 +1445,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
logger.debug("Failed to prepare ready bypassed template: {} on primary storage: {}", srcTemplateInfo, templateOnPrimary);
|
||||
throw new CloudRuntimeException(String.format("Failed to prepare ready bypassed template: %s on primary storage: %s", srcTemplateInfo, templateOnPrimary));
|
||||
}
|
||||
templateOnPrimary.processEvent(Event.OperationSuccessed);
|
||||
templateOnPrimary.processEvent(Event.OperationSucceeded);
|
||||
return templateOnPrimaryNow;
|
||||
} finally {
|
||||
revokeAccess(templateOnPrimary, destHost, destPrimaryDataStore);
|
||||
|
|
@ -1700,7 +1700,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
apiResult.setResult(result.getResult());
|
||||
event = Event.OperationFailed;
|
||||
} else {
|
||||
event = Event.OperationSuccessed;
|
||||
event = Event.OperationSucceeded;
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -1806,8 +1806,8 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
return null;
|
||||
}
|
||||
|
||||
srcVolume.processEvent(Event.OperationSuccessed);
|
||||
destVolume.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
srcVolume.processEvent(Event.OperationSucceeded);
|
||||
destVolume.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
srcVolume.getDataStore().delete(srcVolume);
|
||||
future.complete(res);
|
||||
} catch (Exception e) {
|
||||
|
|
@ -1857,8 +1857,8 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
res.setResult(result.getResult());
|
||||
future.complete(res);
|
||||
} else {
|
||||
srcVolume.processEvent(Event.OperationSuccessed); // back to Ready state in Volume table
|
||||
destVolume.processEventOnly(Event.OperationSuccessed, result.getAnswer());
|
||||
srcVolume.processEvent(Event.OperationSucceeded); // back to Ready state in Volume table
|
||||
destVolume.processEventOnly(Event.OperationSucceeded, result.getAnswer());
|
||||
future.complete(res);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -1940,7 +1940,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
if (destVolume.getStoragePoolType() == StoragePoolType.PowerFlex) {
|
||||
logger.info("Dest volume {} can be removed", destVolume);
|
||||
destVolume.processEvent(Event.ExpungeRequested);
|
||||
destVolume.processEvent(Event.OperationSuccessed);
|
||||
destVolume.processEvent(Event.OperationSucceeded);
|
||||
volDao.remove(destVolume.getId());
|
||||
future.complete(res);
|
||||
return null;
|
||||
|
|
@ -1973,7 +1973,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
|
||||
protected boolean destroySourceVolumeAfterMigration(Event destinationEvent, Answer destinationEventAnswer, VolumeInfo sourceVolume,
|
||||
VolumeInfo destinationVolume, boolean retryExpungeVolumeAsync) {
|
||||
sourceVolume.processEvent(Event.OperationSuccessed);
|
||||
sourceVolume.processEvent(Event.OperationSucceeded);
|
||||
destinationVolume.processEvent(destinationEvent, destinationEventAnswer);
|
||||
|
||||
VolumeVO sourceVolumeVo = ((VolumeObject) sourceVolume).getVolume();
|
||||
|
|
@ -1989,7 +1989,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
if (sourceVolume.getStoragePoolType() == StoragePoolType.PowerFlex) {
|
||||
logger.info("Source volume {} can be removed.", sourceVolumeVo);
|
||||
sourceVolume.processEvent(Event.ExpungeRequested);
|
||||
sourceVolume.processEvent(Event.OperationSuccessed);
|
||||
sourceVolume.processEvent(Event.OperationSucceeded);
|
||||
volDao.remove(sourceVolume.getId());
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2084,7 +2084,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
logger.debug("Failed to create dest volume {}, volume can be removed", destVolume);
|
||||
destroyVolume(destVolume.getId());
|
||||
destVolume.processEvent(Event.ExpungeRequested);
|
||||
destVolume.processEvent(Event.OperationSuccessed);
|
||||
destVolume.processEvent(Event.OperationSucceeded);
|
||||
volDao.remove(destVolume.getId());
|
||||
throw new CloudRuntimeException("Creation of a dest volume failed: " + createVolumeResult.getResult());
|
||||
}
|
||||
|
|
@ -2173,7 +2173,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
logger.debug("failed to clean up managed volume on storage", e);
|
||||
}
|
||||
} else {
|
||||
srcVolume.processEvent(Event.OperationSuccessed);
|
||||
srcVolume.processEvent(Event.OperationSucceeded);
|
||||
destVolume.processEvent(Event.MigrationCopySucceeded, result.getAnswer());
|
||||
volDao.updateUuid(srcVolume.getId(), destVolume.getId());
|
||||
volDao.detachVolume(srcVolume.getId());
|
||||
|
|
@ -2293,7 +2293,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
srcVolume.processEvent(Event.OperationFailed);
|
||||
future.complete(res);
|
||||
} else {
|
||||
srcVolume.processEvent(Event.OperationSuccessed);
|
||||
srcVolume.processEvent(Event.OperationSucceeded);
|
||||
if (srcVolume.getStoragePoolType() == StoragePoolType.PowerFlex) {
|
||||
future.complete(res);
|
||||
return null;
|
||||
|
|
@ -2380,7 +2380,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
|
||||
VolumeInfo volume = entry.getKey();
|
||||
snapshotMgr.cleanupSnapshotsByVolume(volume.getId());
|
||||
volume.processEvent(Event.OperationSuccessed);
|
||||
volume.processEvent(Event.OperationSucceeded);
|
||||
}
|
||||
future.complete(res);
|
||||
}
|
||||
|
|
@ -2448,7 +2448,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
}
|
||||
|
||||
} else {
|
||||
vo.processEvent(Event.OperationSuccessed, result.getAnswer());
|
||||
vo.processEvent(Event.OperationSucceeded, result.getAnswer());
|
||||
|
||||
if (vo.getSize() != null) {
|
||||
// publish usage events
|
||||
|
|
@ -2568,7 +2568,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
}
|
||||
|
||||
try {
|
||||
volume.processEvent(Event.OperationSuccessed);
|
||||
volume.processEvent(Event.OperationSucceeded);
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to change volume state (after resize success)", e);
|
||||
}
|
||||
|
|
@ -2657,7 +2657,7 @@ public class VolumeServiceImpl implements VolumeService {
|
|||
|
||||
if (volume.getState() == State.NotUploaded || volume.getState() == State.UploadInProgress) {
|
||||
VolumeObject volObj = (VolumeObject)volFactory.getVolume(volume.getId());
|
||||
volObj.processEvent(Event.OperationSuccessed);
|
||||
volObj.processEvent(Event.OperationSucceeded);
|
||||
}
|
||||
|
||||
if (volInfo.getSize() > 0) {
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ public class VolumeObjectTest extends TestCase{
|
|||
expectedResult.put(ObjectInDataStoreStateMachine.Event.MigrationRequested, Volume.Event.CopyRequested);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.DestroyRequested, Volume.Event.DestroyRequested);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.ExpungeRequested, Volume.Event.ExpungingRequested);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.OperationSuccessed, Volume.Event.OperationSucceeded);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.OperationSucceeded, Volume.Event.OperationSucceeded);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.MigrationCopySucceeded, Volume.Event.MigrationCopySucceeded);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.OperationFailed, Volume.Event.OperationFailed);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.MigrationCopyFailed, Volume.Event.MigrationCopyFailed);
|
||||
|
|
@ -177,7 +177,7 @@ public class VolumeObjectTest extends TestCase{
|
|||
expectedResult.put(ObjectInDataStoreStateMachine.Event.MigrationCopyRequested, Volume.Event.MigrationCopyRequested);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.DestroyRequested, Volume.Event.DestroyRequested);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.ExpungeRequested, Volume.Event.ExpungingRequested);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.OperationSuccessed, Volume.Event.OperationSucceeded);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.OperationSucceeded, Volume.Event.OperationSucceeded);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.MigrationCopySucceeded, Volume.Event.MigrationCopySucceeded);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.OperationFailed, Volume.Event.OperationFailed);
|
||||
expectedResult.put(ObjectInDataStoreStateMachine.Event.MigrationCopyFailed, Volume.Event.MigrationCopyFailed);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public class ConfigKeyScheduledExecutionWrapper implements Runnable {
|
|||
this.unit = unit;
|
||||
}
|
||||
|
||||
protected ConfigKeyScheduledExecutionWrapper(ScheduledExecutorService executorService, Runnable command,
|
||||
public ConfigKeyScheduledExecutionWrapper(ScheduledExecutorService executorService, Runnable command,
|
||||
ConfigKey<?> configKey, int enableIntervalSeconds, TimeUnit unit) {
|
||||
validateArgs(executorService, command, configKey);
|
||||
this.executorService = executorService;
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ import org.apache.cloudstack.api.Identity;
|
|||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
public interface Webhook extends ControlledEntity, Identity, InternalIdentity {
|
||||
public static final long ID_DUMMY = 0L;
|
||||
public static final String NAME_DUMMY = "Test";
|
||||
long ID_DUMMY = 0L;
|
||||
String NAME_DUMMY = "Test";
|
||||
enum State {
|
||||
Enabled, Disabled;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,14 +18,18 @@
|
|||
package org.apache.cloudstack.mom.webhook;
|
||||
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.AddWebhookFilterCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.CreateWebhookCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookDeliveryCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookFilterCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.ExecuteWebhookDeliveryCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhookDeliveriesCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhookFiltersCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhooksCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.UpdateWebhookCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse;
|
||||
import org.apache.cloudstack.mom.webhook.api.response.WebhookFilterResponse;
|
||||
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
|
|
@ -41,4 +45,7 @@ public interface WebhookApiService extends PluggableService {
|
|||
ListResponse<WebhookDeliveryResponse> listWebhookDeliveries(ListWebhookDeliveriesCmd cmd);
|
||||
int deleteWebhookDelivery(DeleteWebhookDeliveryCmd cmd) throws CloudRuntimeException;
|
||||
WebhookDeliveryResponse executeWebhookDelivery(ExecuteWebhookDeliveryCmd cmd) throws CloudRuntimeException;
|
||||
ListResponse<WebhookFilterResponse> listWebhookFilters(ListWebhookFiltersCmd cmd) throws CloudRuntimeException;
|
||||
WebhookFilterResponse addWebhookFilter(AddWebhookFilterCmd cmd) throws CloudRuntimeException;
|
||||
int deleteWebhookFilter(DeleteWebhookFilterCmd cmd) throws CloudRuntimeException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,23 +29,30 @@ import org.apache.cloudstack.acl.SecurityChecker;
|
|||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.AddWebhookFilterCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.CreateWebhookCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookDeliveryCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookFilterCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.ExecuteWebhookDeliveryCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhookDeliveriesCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhookFiltersCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhooksCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.command.user.UpdateWebhookCmd;
|
||||
import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse;
|
||||
import org.apache.cloudstack.mom.webhook.api.response.WebhookFilterResponse;
|
||||
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||
import org.apache.cloudstack.mom.webhook.dao.WebhookDao;
|
||||
import org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryDao;
|
||||
import org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryJoinDao;
|
||||
import org.apache.cloudstack.mom.webhook.dao.WebhookFilterDao;
|
||||
import org.apache.cloudstack.mom.webhook.dao.WebhookJoinDao;
|
||||
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryJoinVO;
|
||||
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO;
|
||||
import org.apache.cloudstack.mom.webhook.vo.WebhookFilterVO;
|
||||
import org.apache.cloudstack.mom.webhook.vo.WebhookJoinVO;
|
||||
import org.apache.cloudstack.mom.webhook.vo.WebhookVO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
|
@ -59,6 +66,7 @@ import com.cloud.exception.PermissionDeniedException;
|
|||
import com.cloud.projects.Project;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.utils.EnumUtils;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.UriUtils;
|
||||
|
|
@ -84,6 +92,8 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
@Inject
|
||||
WebhookDeliveryJoinDao webhookDeliveryJoinDao;
|
||||
@Inject
|
||||
WebhookFilterDao webhookFilterDao;
|
||||
@Inject
|
||||
ManagementServerHostDao managementServerHostDao;
|
||||
@Inject
|
||||
WebhookService webhookService;
|
||||
|
|
@ -225,6 +235,25 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
throw new InvalidParameterValueException(error);
|
||||
}
|
||||
|
||||
protected WebhookFilterResponse createWebhookFilterResponse(WebhookFilter webhookFilter, WebhookVO webhookVO) {
|
||||
WebhookFilterResponse response = new WebhookFilterResponse();
|
||||
response.setObjectName("webhookfilter");
|
||||
response.setId(webhookFilter.getUuid());
|
||||
if (webhookVO == null) {
|
||||
webhookVO = webhookDao.findById(webhookFilter.getWebhookId());
|
||||
}
|
||||
if (webhookVO != null) {
|
||||
response.setWebhookId(webhookVO.getUuid());
|
||||
response.setWebhookName(webhookVO.getName());
|
||||
}
|
||||
response.setType(webhookFilter.getType().toString());
|
||||
response.setMode(webhookFilter.getMode().toString());
|
||||
response.setMatchType(webhookFilter.getMatchType().toString());
|
||||
response.setValue(webhookFilter.getValue());
|
||||
response.setCreated(webhookFilter.getCreated());
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListResponse<WebhookResponse> listWebhooks(ListWebhooksCmd cmd) {
|
||||
final CallContext ctx = CallContext.current();
|
||||
|
|
@ -234,6 +263,25 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
final String name = cmd.getName();
|
||||
final String keyword = cmd.getKeyword();
|
||||
final String scopeStr = cmd.getScope();
|
||||
Webhook.Scope scope = null;
|
||||
if (StringUtils.isNotEmpty(scopeStr)) {
|
||||
scope = EnumUtils.getEnumIgnoreCase(Webhook.Scope.class, scopeStr);
|
||||
if (scope == null) {
|
||||
throw new InvalidParameterValueException("Invalid scope specified");
|
||||
}
|
||||
}
|
||||
if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(caller.getType())) ||
|
||||
(Webhook.Scope.Domain.equals(scope) &&
|
||||
!List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(caller.getType()))) {
|
||||
throw new InvalidParameterValueException(String.format("Scope %s can not be specified", scope));
|
||||
}
|
||||
Webhook.State state = null;
|
||||
if (StringUtils.isNotEmpty(stateStr)) {
|
||||
state = EnumUtils.getEnumIgnoreCase(Webhook.State.class, stateStr);
|
||||
if (state == null) {
|
||||
throw new InvalidParameterValueException("Invalid state specified");
|
||||
}
|
||||
}
|
||||
List<WebhookResponse> responsesList = new ArrayList<>();
|
||||
List<Long> permittedAccounts = new ArrayList<>();
|
||||
Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject =
|
||||
|
|
@ -258,27 +306,6 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
SearchCriteria<WebhookJoinVO> sc = sb.create();
|
||||
accountManager.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts,
|
||||
listProjectResourcesCriteria);
|
||||
Webhook.Scope scope = null;
|
||||
if (StringUtils.isNotEmpty(scopeStr)) {
|
||||
try {
|
||||
scope = Webhook.Scope.valueOf(scopeStr);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new InvalidParameterValueException("Invalid scope specified");
|
||||
}
|
||||
}
|
||||
if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(caller.getType())) ||
|
||||
(Webhook.Scope.Domain.equals(scope) &&
|
||||
!List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(caller.getType()))) {
|
||||
throw new InvalidParameterValueException(String.format("Scope %s can not be specified", scope));
|
||||
}
|
||||
Webhook.State state = null;
|
||||
if (StringUtils.isNotEmpty(stateStr)) {
|
||||
try {
|
||||
state = Webhook.State.valueOf(stateStr);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new InvalidParameterValueException("Invalid state specified");
|
||||
}
|
||||
}
|
||||
if (scope != null) {
|
||||
sc.setParameters("scope", scope.name());
|
||||
}
|
||||
|
|
@ -316,9 +343,8 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
final String stateStr = cmd.getState();
|
||||
Webhook.Scope scope = Webhook.Scope.Local;
|
||||
if (StringUtils.isNotEmpty(scopeStr)) {
|
||||
try {
|
||||
scope = Webhook.Scope.valueOf(scopeStr);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
scope = EnumUtils.getEnumIgnoreCase(Webhook.Scope.class, scopeStr);
|
||||
if (scope == null) {
|
||||
throw new InvalidParameterValueException("Invalid scope specified");
|
||||
}
|
||||
}
|
||||
|
|
@ -330,9 +356,8 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
}
|
||||
Webhook.State state = Webhook.State.Enabled;
|
||||
if (StringUtils.isNotEmpty(stateStr)) {
|
||||
try {
|
||||
state = Webhook.State.valueOf(stateStr);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
state = EnumUtils.getEnumIgnoreCase(Webhook.State.class, stateStr);
|
||||
if (state == null) {
|
||||
throw new InvalidParameterValueException("Invalid state specified");
|
||||
}
|
||||
}
|
||||
|
|
@ -353,6 +378,7 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
WebhookVO webhook = new WebhookVO(name, description, state, domainId, owner.getId(), payloadUrl, secretKey,
|
||||
sslVerification, scope);
|
||||
webhook = webhookDao.persist(webhook);
|
||||
webhookService.invalidateWebhooksCache();
|
||||
return createWebhookResponse(webhook.getId());
|
||||
}
|
||||
|
||||
|
|
@ -365,7 +391,11 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
throw new InvalidParameterValueException("Unable to find the webhook with the specified ID");
|
||||
}
|
||||
accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhook);
|
||||
return webhookDao.remove(id);
|
||||
boolean removed = webhookDao.remove(id);
|
||||
if (removed) {
|
||||
webhookService.invalidateWebhooksCache();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -394,29 +424,27 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
updateNeeded = true;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(stateStr)) {
|
||||
try {
|
||||
Webhook.State state = Webhook.State.valueOf(stateStr);
|
||||
webhook.setState(state);
|
||||
updateNeeded = true;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
Webhook.State state = EnumUtils.getEnumIgnoreCase(Webhook.State.class, stateStr);
|
||||
if (state == null) {
|
||||
throw new InvalidParameterValueException("Invalid state specified");
|
||||
}
|
||||
webhook.setState(state);
|
||||
updateNeeded = true;
|
||||
}
|
||||
Account owner = accountManager.getAccount(webhook.getAccountId());
|
||||
if (StringUtils.isNotEmpty(scopeStr)) {
|
||||
try {
|
||||
Webhook.Scope scope = Webhook.Scope.valueOf(scopeStr);
|
||||
if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(owner.getType())) ||
|
||||
(Webhook.Scope.Domain.equals(scope) &&
|
||||
!List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(owner.getType()))) {
|
||||
throw new InvalidParameterValueException(
|
||||
String.format("Scope %s can not be specified for owner %s", scope, owner.getName()));
|
||||
}
|
||||
webhook.setScope(scope);
|
||||
updateNeeded = true;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
Webhook.Scope scope = EnumUtils.getEnumIgnoreCase(Webhook.Scope.class, scopeStr);
|
||||
if (scope == null) {
|
||||
throw new InvalidParameterValueException("Invalid scope specified");
|
||||
}
|
||||
if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(owner.getType())) ||
|
||||
(Webhook.Scope.Domain.equals(scope) &&
|
||||
!List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(owner.getType()))) {
|
||||
throw new InvalidParameterValueException(
|
||||
String.format("Scope %s can not be specified for owner %s", scope, owner.getName()));
|
||||
}
|
||||
webhook.setScope(scope);
|
||||
updateNeeded = true;
|
||||
}
|
||||
URI uri = URI.create(webhook.getPayloadUrl());
|
||||
if (StringUtils.isNotEmpty(payloadUrl)) {
|
||||
|
|
@ -427,7 +455,7 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
updateNeeded = true;
|
||||
}
|
||||
if (sslVerification != null) {
|
||||
if (Boolean.TRUE.equals(sslVerification) && !HttpConstants.HTTPS.equalsIgnoreCase(uri.getScheme())) {
|
||||
if (sslVerification && !HttpConstants.HTTPS.equalsIgnoreCase(uri.getScheme())) {
|
||||
throw new InvalidParameterValueException(
|
||||
String.format("SSL verification can be specified only for HTTPS URLs, %s", payloadUrl));
|
||||
}
|
||||
|
|
@ -444,6 +472,7 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
if (updateNeeded && !webhookDao.update(id, webhook)) {
|
||||
return null;
|
||||
}
|
||||
webhookService.invalidateWebhooksCache();
|
||||
return createWebhookResponse(webhook.getId());
|
||||
}
|
||||
|
||||
|
|
@ -455,8 +484,7 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
|
||||
@Override
|
||||
public ListResponse<WebhookDeliveryResponse> listWebhookDeliveries(ListWebhookDeliveriesCmd cmd) {
|
||||
final CallContext ctx = CallContext.current();
|
||||
final Account caller = ctx.getCallingAccount();
|
||||
final Account caller = CallContext.current().getCallingAccount();
|
||||
final Long id = cmd.getId();
|
||||
final Long webhookId = cmd.getWebhookId();
|
||||
final Long managementServerId = cmd.getManagementServerId();
|
||||
|
|
@ -507,20 +535,23 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
|
||||
@Override
|
||||
public WebhookDeliveryResponse executeWebhookDelivery(ExecuteWebhookDeliveryCmd cmd) throws CloudRuntimeException {
|
||||
final CallContext ctx = CallContext.current();
|
||||
final Account caller = ctx.getCallingAccount();
|
||||
final Account caller = CallContext.current().getCallingAccount();
|
||||
final Long deliveryId = cmd.getId();
|
||||
final Long webhookId = cmd.getWebhookId();
|
||||
final String payloadUrl = getNormalizedPayloadUrl(cmd.getPayloadUrl());
|
||||
final String secretKey = cmd.getSecretKey();
|
||||
final Boolean sslVerification = cmd.isSslVerification();
|
||||
final String payload = cmd.getPayload();
|
||||
final Account owner = accountManager.finalizeOwner(caller, null, null, null);
|
||||
|
||||
if (ObjectUtils.allNull(deliveryId, webhookId) && StringUtils.isBlank(payloadUrl)) {
|
||||
throw new InvalidParameterValueException(String.format("One of the %s, %s or %s must be specified",
|
||||
ApiConstants.ID, ApiConstants.WEBHOOK_ID, ApiConstants.PAYLOAD_URL));
|
||||
}
|
||||
if (deliveryId != null && (webhookId != null || StringUtils.isNotBlank(payloadUrl))) {
|
||||
throw new InvalidParameterValueException(
|
||||
String.format("%s cannot be specified with %s or %s", ApiConstants.ID, ApiConstants.WEBHOOK_ID,
|
||||
ApiConstants.PAYLOAD_URL));
|
||||
}
|
||||
WebhookDeliveryVO existingDelivery = null;
|
||||
WebhookVO webhook = null;
|
||||
if (deliveryId != null) {
|
||||
|
|
@ -545,11 +576,14 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
webhook.setSecretKey(secretKey);
|
||||
}
|
||||
if (sslVerification != null) {
|
||||
webhook.setSslVerification(Boolean.TRUE.equals(sslVerification));
|
||||
webhook.setSslVerification(sslVerification);
|
||||
}
|
||||
}
|
||||
if (webhook != null) {
|
||||
accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhook);
|
||||
}
|
||||
if (ObjectUtils.allNull(deliveryId, webhookId)) {
|
||||
webhook = new WebhookVO(owner.getDomainId(), owner.getId(), payloadUrl, secretKey,
|
||||
webhook = new WebhookVO(caller.getDomainId(), caller.getId(), payloadUrl, secretKey,
|
||||
Boolean.TRUE.equals(sslVerification));
|
||||
}
|
||||
WebhookDelivery webhookDelivery = webhookService.executeWebhookDelivery(existingDelivery, webhook, payload);
|
||||
|
|
@ -559,6 +593,87 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
return createTestWebhookDeliveryResponse(webhookDelivery, webhook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListResponse<WebhookFilterResponse> listWebhookFilters(ListWebhookFiltersCmd cmd) throws CloudRuntimeException {
|
||||
Pair<List<WebhookFilterVO>, Integer> filtersAndCount = webhookFilterDao.searchBy(cmd.getId(), cmd.getWebhookId(),
|
||||
cmd.getStartIndex(), cmd.getPageSizeVal());
|
||||
List<WebhookFilterResponse> responsesList = new ArrayList<>();
|
||||
WebhookVO webhookVO = null;
|
||||
if (filtersAndCount.second() > 0) {
|
||||
webhookVO = webhookDao.findById(filtersAndCount.first().get(0).getWebhookId());
|
||||
}
|
||||
for (WebhookFilterVO filter : filtersAndCount.first()) {
|
||||
WebhookFilterResponse response = createWebhookFilterResponse(filter, webhookVO);
|
||||
responsesList.add(response);
|
||||
}
|
||||
ListResponse<WebhookFilterResponse> response = new ListResponse<>();
|
||||
response.setResponses(responsesList, responsesList.size());
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebhookFilterResponse addWebhookFilter(AddWebhookFilterCmd cmd) throws CloudRuntimeException {
|
||||
final Account caller = CallContext.current().getCallingAccount();
|
||||
final long id = cmd.getId();
|
||||
final String typeStr = cmd.getType();
|
||||
final String modeStr = cmd.getMode();
|
||||
final String matchTypeStr = cmd.getMatchType();
|
||||
final String value = cmd.getValue();
|
||||
WebhookVO webhook = webhookDao.findById(id);
|
||||
if (webhook == null) {
|
||||
throw new InvalidParameterValueException("Unable to find the webhook with the specified ID");
|
||||
}
|
||||
accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhook);
|
||||
WebhookFilter.Type type = EnumUtils.getEnumIgnoreCase(WebhookFilter.Type.class, typeStr, WebhookFilter.Type.EventType);
|
||||
WebhookFilter.Mode mode = WebhookFilter.Mode.Include;
|
||||
if (StringUtils.isNotBlank(modeStr)) {
|
||||
mode = EnumUtils.getEnumIgnoreCase(WebhookFilter.Mode.class, modeStr);
|
||||
if (mode == null) {
|
||||
throw new InvalidParameterValueException("Invalid mode specified");
|
||||
}
|
||||
}
|
||||
WebhookFilter.MatchType matchType = WebhookFilter.MatchType.Exact;
|
||||
if (StringUtils.isNotBlank(matchTypeStr)) {
|
||||
matchType = EnumUtils.getEnumIgnoreCase(WebhookFilter.MatchType.class, matchTypeStr);
|
||||
if (matchType == null) {
|
||||
throw new InvalidParameterValueException("Invalid match type specified");
|
||||
}
|
||||
}
|
||||
WebhookFilterVO webhookFilter = new WebhookFilterVO(webhook.getId(), type, mode, matchType, value);
|
||||
List<? extends WebhookFilter> existingFilters = webhookFilterDao.listByWebhook(webhook.getId());
|
||||
if (CollectionUtils.isNotEmpty(existingFilters)) {
|
||||
WebhookFilter conflicting = webhookFilter.getConflicting(existingFilters);
|
||||
if (conflicting != null) {
|
||||
logger.error("Conflict detected when adding WebhookFilter having type: {}, mode: {}, " +
|
||||
"matchtype: {}, value: {} with existing {} for {}", type, mode, matchType, value, conflicting,
|
||||
webhook);
|
||||
throw new InvalidParameterValueException(String.format("Conflicting Webhook filter exists ID: %s",
|
||||
conflicting.getId()));
|
||||
}
|
||||
}
|
||||
webhookFilter = webhookFilterDao.persist(webhookFilter);
|
||||
webhookService.invalidateWebhookFiltersCache(webhook.getId());
|
||||
return createWebhookFilterResponse(webhookFilter, webhook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteWebhookFilter(DeleteWebhookFilterCmd cmd) throws CloudRuntimeException {
|
||||
final Account caller = CallContext.current().getCallingAccount();
|
||||
final Pair<List<WebhookFilterVO>, Integer> filtersAndCount =
|
||||
webhookFilterDao.searchBy(cmd.getId(), cmd.getWebhookId(), 0L, 1L);
|
||||
if (filtersAndCount.second() == 0) {
|
||||
return 0;
|
||||
}
|
||||
final long webhookId = filtersAndCount.first().get(0).getWebhookId();
|
||||
Webhook webhook = webhookDao.findById(webhookId);
|
||||
accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhook);
|
||||
int result = webhookFilterDao.delete(cmd.getId(), webhookId);
|
||||
if (result > 0) {
|
||||
webhookService.invalidateWebhookFiltersCache(webhookId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getCommands() {
|
||||
List<Class<?>> cmdList = new ArrayList<>();
|
||||
|
|
@ -569,6 +684,9 @@ public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiServ
|
|||
cmdList.add(ListWebhookDeliveriesCmd.class);
|
||||
cmdList.add(DeleteWebhookDeliveryCmd.class);
|
||||
cmdList.add(ExecuteWebhookDeliveryCmd.class);
|
||||
cmdList.add(ListWebhookFiltersCmd.class);
|
||||
cmdList.add(AddWebhookFilterCmd.class);
|
||||
cmdList.add(DeleteWebhookFilterCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
// 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.mom.webhook;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
public interface WebhookFilter extends Identity, InternalIdentity {
|
||||
|
||||
enum Type {
|
||||
EventType
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
Include, Exclude
|
||||
}
|
||||
|
||||
enum MatchType {
|
||||
Exact, Prefix, Suffix, Contains
|
||||
}
|
||||
|
||||
long getId();
|
||||
long getWebhookId();
|
||||
Type getType();
|
||||
Mode getMode();
|
||||
MatchType getMatchType();
|
||||
String getValue();
|
||||
Date getCreated();
|
||||
|
||||
static boolean overlaps(WebhookFilter.MatchType oldMatchType, String oldValue, WebhookFilter.MatchType newMatchType, String newValue) {
|
||||
switch (oldMatchType) {
|
||||
case Exact:
|
||||
switch (newMatchType) {
|
||||
case Exact:
|
||||
return oldValue.equals(newValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case Prefix:
|
||||
switch (newMatchType) {
|
||||
case Exact:
|
||||
case Prefix:
|
||||
return newValue.startsWith(oldValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case Suffix:
|
||||
switch (newMatchType) {
|
||||
case Exact:
|
||||
case Suffix:
|
||||
return newValue.endsWith(oldValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case Contains:
|
||||
switch (newMatchType) {
|
||||
case Exact:
|
||||
case Prefix:
|
||||
case Suffix:
|
||||
case Contains:
|
||||
return newValue.contains(oldValue);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default WebhookFilter getConflicting(List<? extends WebhookFilter> existing) {
|
||||
for (WebhookFilter f : existing) {
|
||||
if (f.getType() != this.getType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1. Duplicate entry (same mode, match type, and value)
|
||||
if (f.getMode() == this.getMode()
|
||||
&& f.getMatchType() == this.getMatchType()
|
||||
&& f.getValue().equalsIgnoreCase(this.getValue())) {
|
||||
return f;
|
||||
}
|
||||
|
||||
// 2. Opposite mode (INCLUDE vs EXCLUDE) — check for overlap
|
||||
if (f.getMode() != this.getMode()) {
|
||||
String oldVal = f.getValue().toUpperCase();
|
||||
String newVal = this.getValue().toUpperCase();
|
||||
|
||||
if (overlaps(f.getMatchType(), oldVal, this.getMatchType(), newVal)) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -60,4 +60,6 @@ public interface WebhookService extends PluggableService, Configurable {
|
|||
void handleEvent(Event event) throws EventBusException;
|
||||
WebhookDelivery executeWebhookDelivery(WebhookDelivery delivery, Webhook webhook, String payload)
|
||||
throws CloudRuntimeException;
|
||||
void invalidateWebhooksCache();
|
||||
void invalidateWebhookFiltersCache(long webhookId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
|
@ -32,7 +33,6 @@ import javax.inject.Inject;
|
|||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||
import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.framework.async.AsyncRpcContext;
|
||||
|
|
@ -42,10 +42,14 @@ import org.apache.cloudstack.framework.events.EventBusException;
|
|||
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||
import org.apache.cloudstack.mom.webhook.dao.WebhookDao;
|
||||
import org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryDao;
|
||||
import org.apache.cloudstack.mom.webhook.dao.WebhookFilterDao;
|
||||
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO;
|
||||
import org.apache.cloudstack.mom.webhook.vo.WebhookFilterVO;
|
||||
import org.apache.cloudstack.mom.webhook.vo.WebhookVO;
|
||||
import org.apache.cloudstack.utils.cache.LazyCache;
|
||||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.api.query.vo.EventJoinVO;
|
||||
|
|
@ -75,7 +79,9 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
@Inject
|
||||
WebhookDao webhookDao;
|
||||
@Inject
|
||||
protected WebhookDeliveryDao webhookDeliveryDao;
|
||||
WebhookDeliveryDao webhookDeliveryDao;
|
||||
@Inject
|
||||
WebhookFilterDao webhookFilterDao;
|
||||
@Inject
|
||||
ManagementServerHostDao managementServerHostDao;
|
||||
@Inject
|
||||
|
|
@ -83,6 +89,9 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
@Inject
|
||||
AccountManager accountManager;
|
||||
|
||||
protected LazyCache<org.apache.commons.lang3.tuple.Pair<Long, List<Long>>, List<WebhookVO>> webhooksCache;
|
||||
protected LazyCache<Long, List<WebhookFilterVO>> webhookFiltersCache;
|
||||
|
||||
protected WebhookDeliveryThread getDeliveryJob(Event event, Webhook webhook, Pair<Integer, Integer> configs) {
|
||||
WebhookDeliveryThread.WebhookDeliveryContext<WebhookDeliveryThread.WebhookDeliveryResult> context =
|
||||
new WebhookDeliveryThread.WebhookDeliveryContext<>(null, event.getEventId(), webhook.getId());
|
||||
|
|
@ -97,13 +106,74 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
return job;
|
||||
}
|
||||
|
||||
protected String getEventValueByFilterType(Event event, WebhookFilter.Type filterType) {
|
||||
if (WebhookFilter.Type.EventType.equals(filterType)) {
|
||||
return event.getEventType();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean isValueMatchingFilter(String eventValue, WebhookFilter.MatchType matchType, String filterValue) {
|
||||
switch (matchType) {
|
||||
case Exact:
|
||||
return eventValue.equals(filterValue);
|
||||
case Prefix:
|
||||
return eventValue.startsWith(filterValue);
|
||||
case Suffix:
|
||||
return eventValue.endsWith(filterValue);
|
||||
case Contains:
|
||||
return eventValue.contains(filterValue);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isEventMatchingFilters(Event event, List<? extends WebhookFilter> filters) {
|
||||
if (CollectionUtils.isEmpty(filters)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean hasAnyInclude = false;
|
||||
boolean anyIncludeMatched = false;
|
||||
|
||||
// First pass: short-circuit on any Exclude match; track Include presence/match
|
||||
for (WebhookFilter f : filters) {
|
||||
final WebhookFilter.Type type = f.getType();
|
||||
String eventValue = getEventValueByFilterType(event, type);
|
||||
|
||||
if (f.getMode() == WebhookFilter.Mode.Exclude) {
|
||||
if (eventValue != null && isValueMatchingFilter(eventValue, f.getMatchType(), f.getValue())) {
|
||||
logger.trace("{} matched Exclude {}, webhook delivery will be skipped", event, f);
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f.getMode() == WebhookFilter.Mode.Include) {
|
||||
hasAnyInclude = true;
|
||||
if (!anyIncludeMatched && eventValue != null &&
|
||||
isValueMatchingFilter(eventValue, f.getMatchType(), f.getValue())) {
|
||||
logger.trace("{} matched Include {}", event, f);
|
||||
anyIncludeMatched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there were includes, we must have matched at least one; otherwise allow by default
|
||||
if (hasAnyInclude && !anyIncludeMatched) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected List<Runnable> getDeliveryJobs(Event event) throws EventBusException {
|
||||
List<Runnable> jobs = new ArrayList<>();
|
||||
if (!EventCategory.ACTION_EVENT.getName().equals(event.getEventCategory())) {
|
||||
return jobs;
|
||||
}
|
||||
if (event.getResourceAccountId() == null) {
|
||||
logger.warn("Skipping delivering event {} to any webhook as account ID is missing", event);
|
||||
logger.warn("Skipping delivering {} to any webhook as account ID is missing", event);
|
||||
throw new EventBusException(String.format("Account missing for the event ID: %s", event.getEventUuid()));
|
||||
}
|
||||
List<Long> domainIds = new ArrayList<>();
|
||||
|
|
@ -112,9 +182,14 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
domainIds.addAll(domainDao.getDomainParentIds(event.getResourceDomainId()));
|
||||
}
|
||||
List<WebhookVO> webhooks =
|
||||
webhookDao.listByEnabledForDelivery(event.getResourceAccountId(), domainIds);
|
||||
webhooksCache.get(org.apache.commons.lang3.tuple.Pair.of(event.getResourceAccountId(), domainIds));
|
||||
Map<Long, Pair<Integer, Integer>> domainConfigs = new HashMap<>();
|
||||
for (WebhookVO webhook : webhooks) {
|
||||
List<? extends WebhookFilter> filters = webhookFiltersCache.get(webhook.getId());
|
||||
if (!isEventMatchingFilters(event, filters)) {
|
||||
logger.debug("Skipping delivering {} to {} as it doesn't match filters", event, webhook);
|
||||
continue;
|
||||
}
|
||||
if (!domainConfigs.containsKey(webhook.getDomainId())) {
|
||||
domainConfigs.put(webhook.getDomainId(),
|
||||
new Pair<>(WebhookDeliveryTries.valueIn(webhook.getDomainId()),
|
||||
|
|
@ -128,7 +203,7 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
}
|
||||
|
||||
protected Runnable getManualDeliveryJob(WebhookDelivery existingDelivery, Webhook webhook, String payload,
|
||||
AsyncCallFuture<WebhookDeliveryThread.WebhookDeliveryResult> future) {
|
||||
CompletableFuture<WebhookDeliveryThread.WebhookDeliveryResult> future) {
|
||||
if (StringUtils.isBlank(payload)) {
|
||||
payload = "{ \"CloudStack\": \"works!\" }";
|
||||
}
|
||||
|
|
@ -155,7 +230,7 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
event.setDescription(description);
|
||||
event.setResourceAccountUuid(resourceAccountUuid);
|
||||
ManualDeliveryContext<WebhookDeliveryThread.WebhookDeliveryResult> context =
|
||||
new ManualDeliveryContext<>(null, webhook, future);
|
||||
new ManualDeliveryContext<>(null, future);
|
||||
AsyncCallbackDispatcher<WebhookServiceImpl, WebhookDeliveryThread.WebhookDeliveryResult> caller =
|
||||
AsyncCallbackDispatcher.create(this);
|
||||
caller.setCallback(caller.getTarget().manualDeliveryCompleteCallback(null, null))
|
||||
|
|
@ -181,7 +256,7 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
AsyncCallbackDispatcher<WebhookServiceImpl, WebhookDeliveryThread.WebhookDeliveryResult> callback,
|
||||
ManualDeliveryContext<WebhookDeliveryThread.WebhookDeliveryResult> context) {
|
||||
WebhookDeliveryThread.WebhookDeliveryResult result = callback.getResult();
|
||||
context.future.complete(result);
|
||||
context.getFuture().complete(result);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -205,8 +280,20 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
return processed;
|
||||
}
|
||||
|
||||
protected void initCaches() {
|
||||
webhooksCache = new LazyCache<>(
|
||||
16, 60,
|
||||
(key) -> webhookDao.listByEnabledForDelivery(key.getLeft(), key.getRight())
|
||||
);
|
||||
webhookFiltersCache = new LazyCache<>(
|
||||
16, 60,
|
||||
(webhookId) -> webhookFilterDao.listByWebhook(webhookId)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||
initCaches();
|
||||
try {
|
||||
webhookJobExecutor = Executors.newFixedThreadPool(WebhookDeliveryThreadPoolSize.value(),
|
||||
new NamedThreadFactory(WEBHOOK_JOB_POOL_THREAD_PREFIX));
|
||||
|
|
@ -273,7 +360,7 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
@Override
|
||||
public WebhookDelivery executeWebhookDelivery(WebhookDelivery delivery, Webhook webhook, String payload)
|
||||
throws CloudRuntimeException {
|
||||
AsyncCallFuture<WebhookDeliveryThread.WebhookDeliveryResult> future = new AsyncCallFuture<>();
|
||||
CompletableFuture<WebhookDeliveryThread.WebhookDeliveryResult> future = new CompletableFuture<>();
|
||||
Runnable job = getManualDeliveryJob(delivery, webhook, payload, future);
|
||||
webhookJobExecutor.submit(job);
|
||||
WebhookDeliveryThread.WebhookDeliveryResult result = null;
|
||||
|
|
@ -297,22 +384,33 @@ public class WebhookServiceImpl extends ManagerBase implements WebhookService, W
|
|||
return webhookDeliveryVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateWebhooksCache() {
|
||||
webhooksCache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateWebhookFiltersCache(long webhookId) {
|
||||
webhookFiltersCache.invalidate(webhookId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getCommands() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
static public class ManualDeliveryContext<T> extends AsyncRpcContext<T> {
|
||||
final Webhook webhook;
|
||||
final AsyncCallFuture<WebhookDeliveryThread.WebhookDeliveryResult> future;
|
||||
protected static class ManualDeliveryContext<T> extends AsyncRpcContext<T> {
|
||||
private final CompletableFuture<WebhookDeliveryThread.WebhookDeliveryResult> future;
|
||||
|
||||
public ManualDeliveryContext(AsyncCompletionCallback<T> callback, Webhook webhook,
|
||||
AsyncCallFuture<WebhookDeliveryThread.WebhookDeliveryResult> future) {
|
||||
super(callback);
|
||||
this.webhook = webhook;
|
||||
this.future = future;
|
||||
public CompletableFuture<WebhookDeliveryThread.WebhookDeliveryResult> getFuture() {
|
||||
return future;
|
||||
}
|
||||
|
||||
public ManualDeliveryContext(AsyncCompletionCallback<T> callback,
|
||||
CompletableFuture<WebhookDeliveryThread.WebhookDeliveryResult> future) {
|
||||
super(callback);
|
||||
this.future = future;
|
||||
}
|
||||
}
|
||||
|
||||
public class WebhookDeliveryCleanupWorker extends ManagedContextRunnable {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
// 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.mom.webhook.api.command.user;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||
import org.apache.cloudstack.mom.webhook.WebhookFilter;
|
||||
import org.apache.cloudstack.mom.webhook.api.response.WebhookFilterResponse;
|
||||
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
@APICommand(name = "addWebhookFilter",
|
||||
description = "Adds a Webhook filter",
|
||||
responseObject = WebhookResponse.class,
|
||||
entityType = {WebhookFilter.class},
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false,
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||
since = "4.23.0")
|
||||
public class AddWebhookFilterCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
WebhookApiService webhookApiService;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.WEBHOOK_ID, type = CommandType.UUID, required = true,
|
||||
entityType = WebhookResponse.class, description = "ID for the Webhook")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.MODE, type = BaseCmd.CommandType.STRING,
|
||||
description = "Mode for the Webhook filter - Include or Exclude")
|
||||
private String mode;
|
||||
|
||||
@Parameter(name = ApiConstants.MATCH_TYPE, type = BaseCmd.CommandType.STRING,
|
||||
description = "Match type for the Webhook filter - Exact, Prefix, Suffix or Contains")
|
||||
private String matchType;
|
||||
|
||||
@Parameter(name = ApiConstants.VALUE, type = BaseCmd.CommandType.STRING, required = true,
|
||||
description = "Value for the Webhook which that will be matched")
|
||||
private String value;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccountId();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return WebhookFilter.Type.EventType.name();
|
||||
}
|
||||
|
||||
public String getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public String getMatchType() {
|
||||
return matchType;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ServerApiException {
|
||||
try {
|
||||
WebhookFilterResponse response = webhookApiService.addWebhookFilter(this);
|
||||
if (response == null) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add webhook filter");
|
||||
}
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} catch (CloudRuntimeException ex) {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue