mirror of https://github.com/apache/cloudstack.git
Support dedicating backup offerings to domains (#12194)
* Add support for dedicating backup offerings to domains * Add tests and UI support and update response params * add license header * exclude backupofferingdetailsvo from sonar * fix pre-commit checks - missing / extra EOF line * add test * EOF * filter backup offerings by domain id * add unit tests * add more unit tests and remove response file from code coverage check * update checks * address review comments: extract common code, fix tests * added bean definition * address comments * add unit tests to increase coverage * pre-commit check failure fix * address merge issue * allow updating backup offering when only domain id is modified
This commit is contained in:
parent
002d9768b2
commit
8b2f1f19c2
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,8 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
|
|||
*/
|
||||
BackupOffering importBackupOffering(final ImportBackupOfferingCmd cmd);
|
||||
|
||||
List<Long> getBackupOfferingDomains(final Long offeringId);
|
||||
|
||||
/**
|
||||
* List backup offerings
|
||||
* @param ListBackupOfferingsCmd API 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"));
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,16 @@
|
|||
-- 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';
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ sonar.projectVersion=1.0
|
|||
sonar.sources=src
|
||||
sonar.binaries=target/classes
|
||||
|
||||
# Exclussions
|
||||
# Exclusions
|
||||
sonar.exclusions=**/*Test.java
|
||||
|
||||
# Language
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
|||
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
|
||||
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
|
|
@ -491,6 +492,11 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
|||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(Account account, BackupOffering bof) throws PermissionDeniedException {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Boolean, Map<String, String>> getKeys(GetUserKeysCmd cmd){
|
||||
return null;
|
||||
|
|
|
|||
2
pom.xml
2
pom.xml
|
|
@ -53,6 +53,8 @@
|
|||
<project.systemvm.template.version>4.22.0.0</project.systemvm.template.version>
|
||||
<sonar.organization>apache</sonar.organization>
|
||||
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
||||
<sonar.exclusions>engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingDetailsVO.java</sonar.exclusions>
|
||||
<sonar.exclusions>api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java</sonar.exclusions>
|
||||
|
||||
<!-- Build properties -->
|
||||
<cs.jdk.version>11</cs.jdk.version>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import org.apache.cloudstack.query.QueryService;
|
|||
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DedicatedResourceVO;
|
||||
import com.cloud.dc.dao.DedicatedResourceDao;
|
||||
|
|
@ -70,6 +72,8 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
|
|||
@Inject
|
||||
DomainDao _domainDao;
|
||||
@Inject
|
||||
BackupOfferingDetailsDao backupOfferingDetailsDao;
|
||||
@Inject
|
||||
AccountDao _accountDao;
|
||||
@Inject
|
||||
LaunchPermissionDao _launchPermissionDao;
|
||||
|
|
@ -474,6 +478,35 @@ public class DomainChecker extends AdapterBase implements SecurityChecker {
|
|||
return hasAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(Account account, BackupOffering backupOffering) throws PermissionDeniedException {
|
||||
boolean hasAccess = false;
|
||||
if (account == null || backupOffering == null) {
|
||||
hasAccess = true;
|
||||
} else {
|
||||
if (_accountService.isRootAdmin(account.getId())) {
|
||||
hasAccess = true;
|
||||
}
|
||||
else if (_accountService.isNormalUser(account.getId())
|
||||
|| account.getType() == Account.Type.RESOURCE_DOMAIN_ADMIN
|
||||
|| _accountService.isDomainAdmin(account.getId())
|
||||
|| account.getType() == Account.Type.PROJECT) {
|
||||
final List<Long> boDomainIds = backupOfferingDetailsDao.findDomainIds(backupOffering.getId());
|
||||
if (boDomainIds.isEmpty()) {
|
||||
hasAccess = true;
|
||||
} else {
|
||||
for (Long domainId : boDomainIds) {
|
||||
if (_domainDao.isChildDomain(domainId, account.getDomainId())) {
|
||||
hasAccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAccess(Account account, DataCenter zone) throws PermissionDeniedException {
|
||||
if (account == null || zone.getDomainId() == null) {//public zone
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ import java.util.stream.Collectors;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.consoleproxy.ConsoleProxyManager;
|
||||
import com.cloud.network.router.VirtualNetworkApplianceManager;
|
||||
import com.cloud.storage.secondary.SecondaryStorageVmManager;
|
||||
import com.cloud.utils.DomainHelper;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
|
|
@ -150,7 +155,6 @@ import com.cloud.api.query.vo.NetworkOfferingJoinVO;
|
|||
import com.cloud.capacity.CapacityManager;
|
||||
import com.cloud.capacity.dao.CapacityDao;
|
||||
import com.cloud.configuration.Resource.ResourceType;
|
||||
import com.cloud.consoleproxy.ConsoleProxyManager;
|
||||
import com.cloud.dc.AccountVlanMapVO;
|
||||
import com.cloud.dc.ClusterDetailsDao;
|
||||
import com.cloud.dc.ClusterDetailsVO;
|
||||
|
|
@ -245,7 +249,6 @@ import com.cloud.network.dao.UserIpv6AddressDao;
|
|||
import com.cloud.network.element.NetrisProviderVO;
|
||||
import com.cloud.network.element.NsxProviderVO;
|
||||
import com.cloud.network.netris.NetrisService;
|
||||
import com.cloud.network.router.VirtualNetworkApplianceManager;
|
||||
import com.cloud.network.rules.LoadBalancerContainer.Scheme;
|
||||
import com.cloud.network.vpc.VpcManager;
|
||||
import com.cloud.offering.DiskOffering;
|
||||
|
|
@ -280,7 +283,6 @@ import com.cloud.storage.dao.DiskOfferingDao;
|
|||
import com.cloud.storage.dao.StoragePoolTagsDao;
|
||||
import com.cloud.storage.dao.VMTemplateZoneDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.secondary.SecondaryStorageVmManager;
|
||||
import com.cloud.test.IPRangeConfig;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountDetailVO;
|
||||
|
|
@ -314,7 +316,6 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
|||
import com.cloud.utils.net.NetUtils;
|
||||
import com.cloud.vm.NicIpAlias;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import com.cloud.vm.dao.NicIpAliasDao;
|
||||
import com.cloud.vm.dao.NicIpAliasVO;
|
||||
|
|
@ -399,6 +400,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
ClusterDao _clusterDao;
|
||||
@Inject
|
||||
AlertManager _alertMgr;
|
||||
@Inject
|
||||
DomainHelper domainHelper;
|
||||
List<SecurityChecker> _secChecker;
|
||||
List<ExternalProvisioner> externalProvisioners;
|
||||
|
||||
|
|
@ -3519,7 +3522,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
final boolean isCustomized, final boolean encryptRoot, Long vgpuProfileId, Integer gpuCount, Boolean gpuDisplay, final boolean purgeResources, Integer leaseDuration, VMLeaseManager.ExpiryAction leaseExpiryAction) {
|
||||
|
||||
// Filter child domains when both parent and child domains are present
|
||||
List<Long> filteredDomainIds = filterChildSubDomains(domainIds);
|
||||
List<Long> filteredDomainIds = domainHelper.filterChildSubDomains(domainIds);
|
||||
|
||||
// Check if user exists in the system
|
||||
final User user = _userDao.findById(userId);
|
||||
|
|
@ -3908,7 +3911,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
final Account account = _accountDao.findById(user.getAccountId());
|
||||
|
||||
// Filter child domains when both parent and child domains are present
|
||||
List<Long> filteredDomainIds = filterChildSubDomains(domainIds);
|
||||
List<Long> filteredDomainIds = domainHelper.filterChildSubDomains(domainIds);
|
||||
Collections.sort(filteredDomainIds);
|
||||
|
||||
// avoid domain update of service offering if any instance is associated to it
|
||||
|
|
@ -4118,7 +4121,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
}
|
||||
|
||||
// Filter child domains when both parent and child domains are present
|
||||
List<Long> filteredDomainIds = filterChildSubDomains(domainIds);
|
||||
List<Long> filteredDomainIds = domainHelper.filterChildSubDomains(domainIds);
|
||||
|
||||
// Check if user exists in the system
|
||||
final User user = _userDao.findById(userId);
|
||||
|
|
@ -4394,7 +4397,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
final Account account = _accountDao.findById(user.getAccountId());
|
||||
|
||||
// Filter child domains when both parent and child domains are present
|
||||
List<Long> filteredDomainIds = filterChildSubDomains(domainIds);
|
||||
List<Long> filteredDomainIds = domainHelper.filterChildSubDomains(domainIds);
|
||||
Collections.sort(filteredDomainIds);
|
||||
|
||||
List<Long> filteredZoneIds = new ArrayList<>();
|
||||
|
|
@ -7401,7 +7404,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
}
|
||||
if (offering != null) {
|
||||
// Filter child domains when both parent and child domains are present
|
||||
List<Long> filteredDomainIds = filterChildSubDomains(domainIds);
|
||||
List<Long> filteredDomainIds = domainHelper.filterChildSubDomains(domainIds);
|
||||
List<NetworkOfferingDetailsVO> detailsVO = new ArrayList<>();
|
||||
for (Long domainId : filteredDomainIds) {
|
||||
detailsVO.add(new NetworkOfferingDetailsVO(offering.getId(), Detail.domainid, String.valueOf(domainId), false));
|
||||
|
|
@ -7867,7 +7870,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
}
|
||||
|
||||
// Filter child domains when both parent and child domains are present
|
||||
List<Long> filteredDomainIds = filterChildSubDomains(domainIds);
|
||||
List<Long> filteredDomainIds = domainHelper.filterChildSubDomains(domainIds);
|
||||
Collections.sort(filteredDomainIds);
|
||||
|
||||
List<Long> filteredZoneIds = new ArrayList<>();
|
||||
|
|
@ -8434,30 +8437,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
return false;
|
||||
}
|
||||
|
||||
private List<Long> filterChildSubDomains(final List<Long> domainIds) {
|
||||
List<Long> filteredDomainIds = new ArrayList<>();
|
||||
if (domainIds != null) {
|
||||
filteredDomainIds.addAll(domainIds);
|
||||
}
|
||||
if (filteredDomainIds.size() > 1) {
|
||||
for (int i = filteredDomainIds.size() - 1; i >= 1; i--) {
|
||||
long first = filteredDomainIds.get(i);
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
long second = filteredDomainIds.get(j);
|
||||
if (_domainDao.isChildDomain(filteredDomainIds.get(i), filteredDomainIds.get(j))) {
|
||||
filteredDomainIds.remove(j);
|
||||
i--;
|
||||
}
|
||||
if (_domainDao.isChildDomain(filteredDomainIds.get(j), filteredDomainIds.get(i))) {
|
||||
filteredDomainIds.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredDomainIds;
|
||||
}
|
||||
|
||||
protected void validateCacheMode(String cacheMode){
|
||||
if(cacheMode != null &&
|
||||
!Enums.getIfPresent(DiskOffering.DiskCacheMode.class,
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ import com.cloud.network.element.NetworkACLServiceProvider;
|
|||
import com.cloud.network.element.NsxProviderVO;
|
||||
import com.cloud.network.rules.RulesManager;
|
||||
import com.cloud.network.vpn.RemoteAccessVpnService;
|
||||
import com.cloud.utils.DomainHelper;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
|
|
@ -285,6 +286,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
@Inject
|
||||
DomainDao domainDao;
|
||||
@Inject
|
||||
DomainHelper domainHelper;
|
||||
@Inject
|
||||
private AnnotationDao annotationDao;
|
||||
@Inject
|
||||
NetworkOfferingDao _networkOfferingDao;
|
||||
|
|
@ -636,7 +639,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
}
|
||||
|
||||
// Filter child domains when both parent and child domains are present
|
||||
List<Long> filteredDomainIds = filterChildSubDomains(domainIds);
|
||||
List<Long> filteredDomainIds = domainHelper.filterChildSubDomains(domainIds);
|
||||
|
||||
final Map<Network.Service, Set<Network.Provider>> svcProviderMap = new HashMap<Network.Service, Set<Network.Provider>>();
|
||||
final Set<Network.Provider> defaultProviders = new HashSet<Network.Provider>();
|
||||
|
|
@ -1118,7 +1121,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
|
||||
|
||||
// Filter child domains when both parent and child domains are present
|
||||
List<Long> filteredDomainIds = filterChildSubDomains(domainIds);
|
||||
List<Long> filteredDomainIds = domainHelper.filterChildSubDomains(domainIds);
|
||||
Collections.sort(filteredDomainIds);
|
||||
|
||||
List<Long> filteredZoneIds = new ArrayList<>();
|
||||
|
|
@ -3658,30 +3661,6 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
|||
return _ntwkMgr.areRoutersRunning(routerDao.listByVpcId(vpc.getId()));
|
||||
}
|
||||
|
||||
private List<Long> filterChildSubDomains(final List<Long> domainIds) {
|
||||
List<Long> filteredDomainIds = new ArrayList<>();
|
||||
if (domainIds != null) {
|
||||
filteredDomainIds.addAll(domainIds);
|
||||
}
|
||||
if (filteredDomainIds.size() > 1) {
|
||||
for (int i = filteredDomainIds.size() - 1; i >= 1; i--) {
|
||||
long first = filteredDomainIds.get(i);
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
long second = filteredDomainIds.get(j);
|
||||
if (domainDao.isChildDomain(filteredDomainIds.get(i), filteredDomainIds.get(j))) {
|
||||
filteredDomainIds.remove(j);
|
||||
i--;
|
||||
}
|
||||
if (domainDao.isChildDomain(filteredDomainIds.get(j), filteredDomainIds.get(i))) {
|
||||
filteredDomainIds.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredDomainIds;
|
||||
}
|
||||
|
||||
protected boolean isGlobalAcl(Long aclVpcId) {
|
||||
return aclVpcId != null && aclVpcId == 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupRespon
|
|||
import org.apache.cloudstack.auth.UserAuthenticator;
|
||||
import org.apache.cloudstack.auth.UserAuthenticator.ActionOnFailedAuthentication;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.config.ApiServiceConfiguration;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
|
|
@ -3574,6 +3575,21 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
throw new PermissionDeniedException("There's no way to confirm " + account + " has access to " + vof);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(Account account, BackupOffering bof) throws PermissionDeniedException {
|
||||
for (SecurityChecker checker : _securityCheckers) {
|
||||
if (checker.checkAccess(account, bof)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Access granted to " + account + " to " + bof + " by " + checker.getName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert false : "How can all of the security checkers pass on checking this caller?";
|
||||
throw new PermissionDeniedException("There's no way to confirm " + account + " has access to " + bof);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkAccess(User user, ControlledEntity entity) throws PermissionDeniedException {
|
||||
for (SecurityChecker checker : _securityCheckers) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
// 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 com.cloud.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
|
||||
@Component
|
||||
public class DomainHelper {
|
||||
|
||||
@Inject
|
||||
private DomainDao domainDao;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param domainIds List of domain IDs to filter
|
||||
* @return Filtered list containing only domains that are not descendants of other domains in the list
|
||||
*/
|
||||
public List<Long> filterChildSubDomains(final List<Long> domainIds) {
|
||||
if (domainIds == null || domainIds.size() <= 1) {
|
||||
return domainIds == null ? new ArrayList<>() : new ArrayList<>(domainIds);
|
||||
}
|
||||
|
||||
final List<Long> result = new ArrayList<>();
|
||||
for (final Long candidate : domainIds) {
|
||||
boolean isDescendant = false;
|
||||
for (final Long other : domainIds) {
|
||||
if (Objects.equals(candidate, other)) {
|
||||
continue;
|
||||
}
|
||||
if (domainDao.isChildDomain(other, candidate)) {
|
||||
isDescendant = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isDescendant) {
|
||||
result.add(candidate);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ import java.util.stream.Stream;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.utils.DomainHelper;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
|
@ -68,6 +69,7 @@ import org.apache.cloudstack.api.response.BackupResponse;
|
|||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupDetailsDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupScheduleDao;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
|
@ -81,12 +83,12 @@ import org.apache.cloudstack.poll.BackgroundPollTask;
|
|||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.amazonaws.util.CollectionUtils;
|
||||
import com.cloud.alert.AlertManager;
|
||||
import com.cloud.api.ApiDispatcher;
|
||||
import com.cloud.api.ApiGsonHelper;
|
||||
|
|
@ -184,6 +186,8 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
@Inject
|
||||
private BackupOfferingDao backupOfferingDao;
|
||||
@Inject
|
||||
private BackupOfferingDetailsDao backupOfferingDetailsDao;
|
||||
@Inject
|
||||
private VMInstanceDao vmInstanceDao;
|
||||
@Inject
|
||||
private AccountService accountService;
|
||||
|
|
@ -237,6 +241,8 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
private AlertManager alertManager;
|
||||
@Inject
|
||||
private GuestOSDao _guestOSDao;
|
||||
@Inject
|
||||
private DomainHelper domainHelper;
|
||||
|
||||
private AsyncJobDispatcher asyncJobDispatcher;
|
||||
private Timer backupTimer;
|
||||
|
|
@ -280,6 +286,20 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
throw new CloudRuntimeException("A backup offering with the same name already exists in this zone");
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(cmd.getDomainIds())) {
|
||||
for (final Long domainId: cmd.getDomainIds()) {
|
||||
if (domainDao.findById(domainId) == null) {
|
||||
throw new InvalidParameterValueException("Please specify a valid domain id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Account caller = CallContext.current().getCallingAccount();
|
||||
List<Long> filteredDomainIds = cmd.getDomainIds() == null ? new ArrayList<>() : new ArrayList<>(cmd.getDomainIds());
|
||||
if (filteredDomainIds.size() > 1) {
|
||||
filteredDomainIds = domainHelper.filterChildSubDomains(filteredDomainIds);
|
||||
}
|
||||
|
||||
final BackupProvider provider = getBackupProvider(cmd.getZoneId());
|
||||
if (!provider.isValidProviderOffering(cmd.getZoneId(), cmd.getExternalId())) {
|
||||
throw new CloudRuntimeException("Backup offering '" + cmd.getExternalId() + "' does not exist on provider " + provider.getName() + " on zone " + cmd.getZoneId());
|
||||
|
|
@ -292,15 +312,34 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
if (savedOffering == null) {
|
||||
throw new CloudRuntimeException("Unable to create backup offering: " + cmd.getExternalId() + ", name: " + cmd.getName());
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(filteredDomainIds)) {
|
||||
List<BackupOfferingDetailsVO> detailsVOList = new ArrayList<>();
|
||||
for (Long domainId : filteredDomainIds) {
|
||||
detailsVOList.add(new BackupOfferingDetailsVO(savedOffering.getId(), ApiConstants.DOMAIN_ID, String.valueOf(domainId), false));
|
||||
}
|
||||
if (!detailsVOList.isEmpty()) {
|
||||
backupOfferingDetailsDao.saveDetails(detailsVOList);
|
||||
}
|
||||
}
|
||||
logger.debug("Successfully created backup offering " + cmd.getName() + " mapped to backup provider offering " + cmd.getExternalId());
|
||||
return savedOffering;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getBackupOfferingDomains(Long offeringId) {
|
||||
final BackupOffering backupOffering = backupOfferingDao.findById(offeringId);
|
||||
if (backupOffering == null) {
|
||||
throw new InvalidParameterValueException("Unable to find backup offering for id: " + offeringId);
|
||||
}
|
||||
return backupOfferingDetailsDao.findDomainIds(offeringId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<BackupOffering>, Integer> listBackupOfferings(final ListBackupOfferingsCmd cmd) {
|
||||
final Long offeringId = cmd.getOfferingId();
|
||||
final Long zoneId = cmd.getZoneId();
|
||||
final String keyword = cmd.getKeyword();
|
||||
Long domainId = cmd.getDomainId();
|
||||
|
||||
if (offeringId != null) {
|
||||
BackupOfferingVO offering = backupOfferingDao.findById(offeringId);
|
||||
|
|
@ -314,8 +353,13 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
SearchBuilder<BackupOfferingVO> sb = backupOfferingDao.createSearchBuilder();
|
||||
sb.and("zone_id", sb.entity().getZoneId(), SearchCriteria.Op.EQ);
|
||||
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
|
||||
|
||||
CallContext ctx = CallContext.current();
|
||||
final Account caller = ctx.getCallingAccount();
|
||||
if (Account.Type.ADMIN != caller.getType() && domainId == null) {
|
||||
domainId = caller.getDomainId();
|
||||
}
|
||||
|
||||
if (Account.Type.NORMAL == caller.getType()) {
|
||||
sb.and("user_backups_allowed", sb.entity().isUserDrivenBackupAllowed(), SearchCriteria.Op.EQ);
|
||||
}
|
||||
|
|
@ -328,13 +372,36 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
if (keyword != null) {
|
||||
sc.setParameters("name", "%" + keyword + "%");
|
||||
}
|
||||
|
||||
if (Account.Type.NORMAL == caller.getType()) {
|
||||
sc.setParameters("user_backups_allowed", true);
|
||||
}
|
||||
|
||||
Pair<List<BackupOfferingVO>, Integer> result = backupOfferingDao.searchAndCount(sc, searchFilter);
|
||||
|
||||
if (domainId != null) {
|
||||
List<BackupOfferingVO> filteredOfferings = new ArrayList<>();
|
||||
for (BackupOfferingVO offering : result.first()) {
|
||||
List<Long> offeringDomains = backupOfferingDetailsDao.findDomainIds(offering.getId());
|
||||
if (offeringDomains.isEmpty() || offeringDomains.contains(domainId) || containsParentDomain(offeringDomains, domainId)) {
|
||||
filteredOfferings.add(offering);
|
||||
}
|
||||
}
|
||||
return new Pair<>(new ArrayList<>(filteredOfferings), filteredOfferings.size());
|
||||
}
|
||||
|
||||
return new Pair<>(new ArrayList<>(result.first()), result.second());
|
||||
}
|
||||
|
||||
private boolean containsParentDomain(List<Long> offeringDomains, Long domainId) {
|
||||
for (Long offeringDomainId : offeringDomains) {
|
||||
if (domainDao.isChildDomain(offeringDomainId, domainId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteBackupOffering(final Long offeringId) {
|
||||
final BackupOfferingVO offering = backupOfferingDao.findById(offeringId);
|
||||
|
|
@ -342,6 +409,8 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
throw new CloudRuntimeException("Could not find a backup offering with id: " + offeringId);
|
||||
}
|
||||
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), offering);
|
||||
|
||||
if (backupDao.listByOfferingId(offering.getId()).size() > 0) {
|
||||
throw new CloudRuntimeException("Backup Offering cannot be removed as it has backups associated with it.");
|
||||
}
|
||||
|
|
@ -452,6 +521,12 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
throw new CloudRuntimeException("Provided backup offering does not exist");
|
||||
}
|
||||
|
||||
Account owner = accountManager.getAccount(vm.getAccountId());
|
||||
if (owner == null) {
|
||||
throw new CloudRuntimeException("Unable to find the owner of the VM");
|
||||
}
|
||||
accountManager.checkAccess(owner, offering);
|
||||
|
||||
final BackupProvider backupProvider = getBackupProvider(offering.getProvider());
|
||||
if (backupProvider == null) {
|
||||
throw new CloudRuntimeException("Failed to get the backup provider for the zone, please contact the administrator");
|
||||
|
|
@ -762,10 +837,11 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
@ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_CREATE, eventDescription = "creating VM backup", async = true)
|
||||
public boolean createBackup(CreateBackupCmd cmd, Object job) throws ResourceAllocationException {
|
||||
Long vmId = cmd.getVmId();
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
||||
final VMInstanceVO vm = findVmById(vmId);
|
||||
validateBackupForZone(vm.getDataCenterId());
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm);
|
||||
accountManager.checkAccess(caller, null, true, vm);
|
||||
|
||||
if (vm.getBackupOfferingId() == null) {
|
||||
throw new CloudRuntimeException("VM has not backup offering configured, cannot create backup before assigning it to a backup offering");
|
||||
|
|
@ -1065,7 +1141,7 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
}
|
||||
|
||||
// This is done to handle historic backups if any with Veeam / Networker plugins
|
||||
List<Backup.VolumeInfo> backupVolumes = CollectionUtils.isNullOrEmpty(backup.getBackedUpVolumes()) ?
|
||||
List<Backup.VolumeInfo> backupVolumes = CollectionUtils.isEmpty(backup.getBackedUpVolumes()) ?
|
||||
vm.getBackupVolumeList() : backup.getBackedUpVolumes();
|
||||
List<VolumeVO> vmVolumes = volumeDao.findByInstance(vm.getId());
|
||||
if (vmVolumes.size() != backupVolumes.size()) {
|
||||
|
|
@ -2112,11 +2188,15 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
String name = updateBackupOfferingCmd.getName();
|
||||
String description = updateBackupOfferingCmd.getDescription();
|
||||
Boolean allowUserDrivenBackups = updateBackupOfferingCmd.getAllowUserDrivenBackups();
|
||||
List<Long> domainIds = updateBackupOfferingCmd.getDomainIds();
|
||||
|
||||
BackupOfferingVO backupOfferingVO = backupOfferingDao.findById(id);
|
||||
if (backupOfferingVO == null) {
|
||||
throw new InvalidParameterValueException(String.format("Unable to find Backup Offering with id: [%s].", id));
|
||||
}
|
||||
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), backupOfferingVO);
|
||||
|
||||
logger.debug("Trying to update Backup Offering {} to {}.",
|
||||
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(backupOfferingVO, "uuid", "name", "description", "userDrivenBackupAllowed"),
|
||||
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(updateBackupOfferingCmd, "name", "description", "allowUserDrivenBackups"));
|
||||
|
|
@ -2139,16 +2219,43 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
fields.add("allowUserDrivenBackups: " + allowUserDrivenBackups);
|
||||
}
|
||||
|
||||
if (!backupOfferingDao.update(id, offering)) {
|
||||
if (CollectionUtils.isNotEmpty(domainIds)) {
|
||||
for (final Long domainId: domainIds) {
|
||||
if (domainDao.findById(domainId) == null) {
|
||||
throw new InvalidParameterValueException("Please specify a valid domain id");
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Long> filteredDomainIds = domainHelper.filterChildSubDomains(domainIds);
|
||||
Collections.sort(filteredDomainIds);
|
||||
|
||||
boolean success = backupOfferingDao.update(id, offering);
|
||||
if (!success) {
|
||||
logger.warn(String.format("Couldn't update Backup offering (%s) with [%s].", backupOfferingVO, String.join(", ", fields)));
|
||||
}
|
||||
|
||||
if (success || fields.isEmpty()) {
|
||||
List<Long> existingDomainIds = backupOfferingDetailsDao.findDomainIds(id);
|
||||
Collections.sort(existingDomainIds);
|
||||
updateBackupOfferingDomainDetails(id, filteredDomainIds, existingDomainIds);
|
||||
}
|
||||
|
||||
BackupOfferingVO response = backupOfferingDao.findById(id);
|
||||
CallContext.current().setEventDetails(String.format("Backup Offering updated [%s].",
|
||||
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(response, "id", "name", "description", "userDrivenBackupAllowed", "externalId")));
|
||||
return response;
|
||||
}
|
||||
|
||||
private void updateBackupOfferingDomainDetails(Long id, List<Long> filteredDomainIds, List<Long> existingDomainIds) {
|
||||
if (existingDomainIds == null) {
|
||||
existingDomainIds = new ArrayList<>();
|
||||
}
|
||||
|
||||
if(!filteredDomainIds.equals(existingDomainIds)) {
|
||||
backupOfferingDetailsDao.updateBackupOfferingDomainIdsDetail(id, filteredDomainIds);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> getDetailsFromBackupDetails(Long backupId) {
|
||||
Map<String, String> details = backupDetailsDao.listDetailsKeyPairs(backupId, true);
|
||||
if (details == null) {
|
||||
|
|
@ -2270,7 +2377,7 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
return;
|
||||
}
|
||||
List<Backup> backupsForVm = backupDao.listByVmIdAndOffering(vm.getDataCenterId(), vm.getId(), vm.getBackupOfferingId());
|
||||
if (org.apache.commons.collections.CollectionUtils.isEmpty(backupsForVm)) {
|
||||
if (CollectionUtils.isEmpty(backupsForVm)) {
|
||||
removeVMFromBackupOffering(vm.getId(), true);
|
||||
} else {
|
||||
throw new CloudRuntimeException(String.format("This Instance [uuid: %s, name: %s] has a "
|
||||
|
|
|
|||
|
|
@ -81,4 +81,6 @@
|
|||
|
||||
<bean id="DPDKHelper" class="com.cloud.hypervisor.kvm.dpdk.DpdkHelperImpl" />
|
||||
|
||||
<bean id="domainHelper" class="com.cloud.utils.DomainHelper" />
|
||||
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ package com.cloud.acl;
|
|||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.backup.BackupOfferingVO;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
|
|
@ -35,6 +38,8 @@ import com.cloud.user.AccountVO;
|
|||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.Ternary;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DomainCheckerTest {
|
||||
|
||||
|
|
@ -46,6 +51,8 @@ public class DomainCheckerTest {
|
|||
DomainDao _domainDao;
|
||||
@Mock
|
||||
ProjectManager _projectMgr;
|
||||
@Mock
|
||||
BackupOfferingDetailsDao backupOfferingDetailsDao;
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
|
|
@ -163,4 +170,42 @@ public class DomainCheckerTest {
|
|||
domainChecker.validateCallerHasAccessToEntityOwner(caller, entity, SecurityChecker.AccessType.ListEntry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackupOfferingAccessRootAdmin() {
|
||||
Account rootAdmin = Mockito.mock(Account.class);
|
||||
Mockito.when(rootAdmin.getId()).thenReturn(1L);
|
||||
BackupOfferingVO backupOfferingVO = Mockito.mock(BackupOfferingVO.class);
|
||||
Mockito.when(_accountService.isRootAdmin(rootAdmin.getId())).thenReturn(true);
|
||||
|
||||
boolean hasAccess = domainChecker.checkAccess(rootAdmin, backupOfferingVO);
|
||||
Assert.assertTrue(hasAccess);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackupOfferingAccessDomainAdmin() {
|
||||
Account domainAdmin = Mockito.mock(Account.class);
|
||||
Mockito.when(domainAdmin.getId()).thenReturn(2L);
|
||||
BackupOfferingVO backupOfferingVO = Mockito.mock(BackupOfferingVO.class);
|
||||
AccountVO owner = Mockito.mock(AccountVO.class);
|
||||
Mockito.when(_accountService.isDomainAdmin(domainAdmin.getId())).thenReturn(true);
|
||||
Mockito.when(domainAdmin.getDomainId()).thenReturn(10L);
|
||||
Mockito.when(_domainDao.isChildDomain(100L, 10L)).thenReturn(true);
|
||||
Mockito.when(backupOfferingDetailsDao.findDomainIds(backupOfferingVO.getId())).thenReturn(Collections.singletonList(100L));
|
||||
|
||||
boolean hasAccess = domainChecker.checkAccess(domainAdmin, backupOfferingVO);
|
||||
Assert.assertTrue(hasAccess);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackupOfferingAccessNoAccess() {
|
||||
Account normalUser = Mockito.mock(Account.class);
|
||||
Mockito.when(normalUser.getId()).thenReturn(3L);
|
||||
BackupOfferingVO backupOfferingVO = Mockito.mock(BackupOfferingVO.class);
|
||||
Mockito.when(_accountService.isRootAdmin(normalUser.getId())).thenReturn(false);
|
||||
Mockito.when(_accountService.isDomainAdmin(normalUser.getId())).thenReturn(false);
|
||||
|
||||
boolean hasAccess = domainChecker.checkAccess(normalUser, backupOfferingVO);
|
||||
Assert.assertFalse(hasAccess);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import com.cloud.storage.dao.VolumeDao;
|
|||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManagerImpl;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.DomainHelper;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
|
@ -178,6 +179,8 @@ public class ConfigurationManagerImplTest {
|
|||
PrimaryDataStoreDao storagePoolDao;
|
||||
@Mock
|
||||
StoragePoolDetailsDao storagePoolDetailsDao;
|
||||
@Mock
|
||||
DomainHelper domainHelper;
|
||||
|
||||
DeleteZoneCmd deleteZoneCmd;
|
||||
CreateNetworkOfferingCmd createNetworkOfferingCmd;
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import java.util.Map;
|
|||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.storage.dao.SnapshotPolicyDao;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
|
|
@ -3107,7 +3108,7 @@ public class UserVmManagerImplTest {
|
|||
|
||||
configureDoNothingForMethodsThatWeDoNotWantToTest();
|
||||
|
||||
doThrow(PermissionDeniedException.class).when(accountManager).checkAccess(Mockito.any(Account.class), Mockito.any());
|
||||
doThrow(PermissionDeniedException.class).when(accountManager).checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class));
|
||||
|
||||
Assert.assertThrows(PermissionDeniedException.class, () -> userVmManagerImpl.moveVmToUser(assignVmCmdMock));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ import com.cloud.user.ResourceLimitService;
|
|||
import com.cloud.user.User;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.DomainHelper;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
|
@ -80,11 +81,13 @@ import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd;
|
|||
import org.apache.cloudstack.api.command.user.backup.CreateBackupCmd;
|
||||
import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd;
|
||||
import org.apache.cloudstack.api.command.user.backup.ListBackupScheduleCmd;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupDetailsDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDetailsDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupScheduleDao;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
|
@ -241,6 +244,12 @@ public class BackupManagerTest {
|
|||
@Mock
|
||||
private GuestOSDao _guestOSDao;
|
||||
|
||||
@Mock
|
||||
private BackupOfferingDetailsDao backupOfferingDetailsDao;
|
||||
|
||||
@Mock
|
||||
DomainHelper domainHelper;
|
||||
|
||||
private Gson gson;
|
||||
|
||||
private String[] hostPossibleValues = {"127.0.0.1", "hostname"};
|
||||
|
|
@ -352,6 +361,7 @@ public class BackupManagerTest {
|
|||
when(cmd.getName()).thenReturn("New name");
|
||||
when(cmd.getDescription()).thenReturn("New description");
|
||||
when(cmd.getAllowUserDrivenBackups()).thenReturn(true);
|
||||
when(backupOfferingDetailsDao.findDomainIds(id)).thenReturn(Collections.emptyList());
|
||||
|
||||
BackupOffering updated = backupManager.updateBackupOffering(cmd);
|
||||
assertEquals("New name", updated.getName());
|
||||
|
|
@ -1081,7 +1091,7 @@ public class BackupManagerTest {
|
|||
|
||||
assertEquals("root-disk-offering-uuid", VmDiskInfo.getDiskOffering().getUuid());
|
||||
assertEquals(Long.valueOf(5), VmDiskInfo.getSize());
|
||||
assertEquals(null, VmDiskInfo.getDeviceId());
|
||||
assertNull(VmDiskInfo.getDeviceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1106,7 +1116,7 @@ public class BackupManagerTest {
|
|||
|
||||
assertEquals("Test Offering", result.getName());
|
||||
assertEquals("Test Description", result.getDescription());
|
||||
assertEquals(true, result.isUserDrivenBackupAllowed());
|
||||
assertTrue(result.isUserDrivenBackupAllowed());
|
||||
assertEquals("external-id", result.getExternalId());
|
||||
assertEquals("testbackupprovider", result.getProvider());
|
||||
}
|
||||
|
|
@ -1149,6 +1159,8 @@ public class BackupManagerTest {
|
|||
VMInstanceVO vm = mock(VMInstanceVO.class);
|
||||
when(vm.getId()).thenReturn(vmId);
|
||||
BackupOfferingVO offering = mock(BackupOfferingVO.class);
|
||||
Account owner = mock(Account.class);
|
||||
|
||||
|
||||
overrideBackupFrameworkConfigValue();
|
||||
|
||||
|
|
@ -1159,6 +1171,8 @@ public class BackupManagerTest {
|
|||
when(vm.getBackupOfferingId()).thenReturn(null);
|
||||
when(offering.getProvider()).thenReturn("testbackupprovider");
|
||||
when(backupProvider.assignVMToBackupOffering(vm, offering)).thenReturn(true);
|
||||
when(vm.getAccountId()).thenReturn(3L);
|
||||
when(accountManager.getAccount(vm.getAccountId())).thenReturn(owner);
|
||||
when(vmInstanceDao.update(1L, vm)).thenReturn(true);
|
||||
|
||||
try (MockedStatic<UsageEventUtils> ignored2 = Mockito.mockStatic(UsageEventUtils.class)) {
|
||||
|
|
@ -2156,4 +2170,352 @@ public class BackupManagerTest {
|
|||
verify(vmInstanceDao, times(1)).findByIdIncludingRemoved(vmId);
|
||||
verify(volumeDao, times(1)).findByInstance(vmId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBackupOfferingDomainsTestOfferingNotFound() {
|
||||
Long offeringId = 1L;
|
||||
when(backupOfferingDao.findById(offeringId)).thenReturn(null);
|
||||
|
||||
InvalidParameterValueException exception = Assert.assertThrows(InvalidParameterValueException.class,
|
||||
() -> backupManager.getBackupOfferingDomains(offeringId));
|
||||
assertEquals("Unable to find backup offering for id: " + offeringId, exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBackupOfferingDomainsTestReturnsDomains() {
|
||||
Long offeringId = 1L;
|
||||
BackupOfferingVO offering = Mockito.mock(BackupOfferingVO.class);
|
||||
when(backupOfferingDao.findById(offeringId)).thenReturn(offering);
|
||||
when(backupOfferingDetailsDao.findDomainIds(offeringId)).thenReturn(List.of(10L, 20L));
|
||||
|
||||
List<Long> result = backupManager.getBackupOfferingDomains(offeringId);
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.contains(10L));
|
||||
assertTrue(result.contains(20L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateBackupOfferingThrowsWhenDomainIdInvalid() {
|
||||
Long id = 1234L;
|
||||
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
|
||||
when(cmd.getId()).thenReturn(id);
|
||||
when(cmd.getDomainIds()).thenReturn(List.of(99L));
|
||||
|
||||
when(domainDao.findById(99L)).thenReturn(null);
|
||||
|
||||
InvalidParameterValueException exception = Assert.assertThrows(InvalidParameterValueException.class,
|
||||
() -> backupManager.updateBackupOffering(cmd));
|
||||
assertEquals("Please specify a valid domain id", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateBackupOfferingPersistsDomainDetailsWhenProvided() {
|
||||
Long id = 1234L;
|
||||
Long domainId = 11L;
|
||||
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
|
||||
when(cmd.getId()).thenReturn(id);
|
||||
when(cmd.getDomainIds()).thenReturn(List.of(domainId));
|
||||
|
||||
DomainVO domain = Mockito.mock(DomainVO.class);
|
||||
when(domainDao.findById(domainId)).thenReturn(domain);
|
||||
|
||||
when(domainHelper.filterChildSubDomains(List.of(domainId))).thenReturn(new ArrayList<>(List.of(domainId)));
|
||||
when(backupOfferingDetailsDao.findDomainIds(id)).thenReturn(new ArrayList<>());
|
||||
|
||||
BackupOfferingVO offering = Mockito.mock(BackupOfferingVO.class);
|
||||
BackupOfferingVO offeringUpdate = Mockito.mock(BackupOfferingVO.class);
|
||||
when(backupOfferingDao.findById(id)).thenReturn(offering);
|
||||
when(backupOfferingDao.createForUpdate(id)).thenReturn(offeringUpdate);
|
||||
when(backupOfferingDao.update(id, offeringUpdate)).thenReturn(true);
|
||||
|
||||
BackupOffering updated = backupManager.updateBackupOffering(cmd);
|
||||
|
||||
verify(backupOfferingDetailsDao, times(1)).updateBackupOfferingDomainIdsDetail(id, List.of(domainId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListBackupOfferingsWithDomainFilteringIncludesGlobalOfferings() {
|
||||
Long requestedDomainId = 3L;
|
||||
|
||||
ListBackupOfferingsCmd cmd =
|
||||
Mockito.mock(ListBackupOfferingsCmd.class);
|
||||
when(cmd.getOfferingId()).thenReturn(null);
|
||||
when(cmd.getDomainId()).thenReturn(requestedDomainId);
|
||||
when(cmd.getStartIndex()).thenReturn(0L);
|
||||
when(cmd.getPageSizeVal()).thenReturn(20L);
|
||||
|
||||
BackupOfferingVO globalOffering = createMockOffering(1L, "Global Offering");
|
||||
BackupOfferingVO domainOffering = createMockOffering(2L, "Domain Offering");
|
||||
|
||||
List<BackupOfferingVO> allOfferings = List.of(globalOffering, domainOffering);
|
||||
|
||||
SearchBuilder<BackupOfferingVO> sb = Mockito.mock(SearchBuilder.class);
|
||||
SearchCriteria<BackupOfferingVO> sc = Mockito.mock(SearchCriteria.class);
|
||||
BackupOfferingVO entityMock = Mockito.mock(BackupOfferingVO.class);
|
||||
when(backupOfferingDao.createSearchBuilder()).thenReturn(sb);
|
||||
when(sb.entity()).thenReturn(entityMock);
|
||||
when(sb.and(Mockito.anyString(), Mockito.any(), Mockito.any(SearchCriteria.Op.class))).thenReturn(sb);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(backupOfferingDao.searchAndCount(Mockito.any(), Mockito.any()))
|
||||
.thenReturn(new Pair<>(allOfferings, allOfferings.size()));
|
||||
|
||||
when(backupOfferingDetailsDao.findDomainIds(1L)).thenReturn(Collections.emptyList());
|
||||
when(backupOfferingDetailsDao.findDomainIds(2L)).thenReturn(List.of(2L));
|
||||
|
||||
Account account = Mockito.mock(Account.class);
|
||||
when(account.getType()).thenReturn(Account.Type.NORMAL);
|
||||
|
||||
try (MockedStatic<CallContext> mockedCallContext = Mockito.mockStatic(CallContext.class)) {
|
||||
CallContext contextMock = Mockito.mock(CallContext.class);
|
||||
mockedCallContext.when(CallContext::current).thenReturn(contextMock);
|
||||
when(contextMock.getCallingAccount()).thenReturn(account);
|
||||
|
||||
Pair<List<BackupOffering>, Integer> result = backupManager.listBackupOfferings(cmd);
|
||||
|
||||
assertEquals(1, result.first().size());
|
||||
assertEquals("Global Offering", result.first().get(0).getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListBackupOfferingsWithDomainFilteringIncludesDirectDomainMapping() {
|
||||
Long requestedDomainId = 3L;
|
||||
|
||||
ListBackupOfferingsCmd cmd =
|
||||
Mockito.mock(ListBackupOfferingsCmd.class);
|
||||
when(cmd.getOfferingId()).thenReturn(null);
|
||||
when(cmd.getDomainId()).thenReturn(requestedDomainId);
|
||||
when(cmd.getStartIndex()).thenReturn(0L);
|
||||
when(cmd.getPageSizeVal()).thenReturn(20L);
|
||||
|
||||
BackupOfferingVO directDomainOffering = createMockOffering(1L, "Direct Domain Offering");
|
||||
BackupOfferingVO otherDomainOffering = createMockOffering(2L, "Other Domain Offering");
|
||||
|
||||
List<BackupOfferingVO> allOfferings = List.of(directDomainOffering, otherDomainOffering);
|
||||
|
||||
SearchBuilder<BackupOfferingVO> sb = Mockito.mock(SearchBuilder.class);
|
||||
SearchCriteria<BackupOfferingVO> sc = Mockito.mock(SearchCriteria.class);
|
||||
BackupOfferingVO entityMock = Mockito.mock(BackupOfferingVO.class);
|
||||
when(backupOfferingDao.createSearchBuilder()).thenReturn(sb);
|
||||
when(sb.entity()).thenReturn(entityMock);
|
||||
when(sb.and(Mockito.anyString(), Mockito.any(), Mockito.any(SearchCriteria.Op.class))).thenReturn(sb);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(backupOfferingDao.searchAndCount(Mockito.any(), Mockito.any()))
|
||||
.thenReturn(new Pair<>(allOfferings, allOfferings.size()));
|
||||
|
||||
when(backupOfferingDetailsDao.findDomainIds(1L)).thenReturn(List.of(requestedDomainId));
|
||||
when(backupOfferingDetailsDao.findDomainIds(2L)).thenReturn(List.of(5L));
|
||||
|
||||
Account account = Mockito.mock(Account.class);
|
||||
when(account.getType()).thenReturn(Account.Type.NORMAL);
|
||||
|
||||
try (MockedStatic<CallContext> mockedCallContext = Mockito.mockStatic(CallContext.class)) {
|
||||
CallContext contextMock = Mockito.mock(CallContext.class);
|
||||
mockedCallContext.when(CallContext::current).thenReturn(contextMock);
|
||||
when(contextMock.getCallingAccount()).thenReturn(account);
|
||||
|
||||
Pair<List<BackupOffering>, Integer> result = backupManager.listBackupOfferings(cmd);
|
||||
|
||||
assertEquals(1, result.first().size());
|
||||
assertEquals("Direct Domain Offering", result.first().get(0).getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListBackupOfferingsWithDomainFilteringIncludesParentDomainOfferings() {
|
||||
Long parentDomainId = 1L;
|
||||
Long childDomainId = 3L;
|
||||
|
||||
ListBackupOfferingsCmd cmd =
|
||||
Mockito.mock(ListBackupOfferingsCmd.class);
|
||||
when(cmd.getOfferingId()).thenReturn(null);
|
||||
when(cmd.getDomainId()).thenReturn(childDomainId);
|
||||
when(cmd.getStartIndex()).thenReturn(0L);
|
||||
when(cmd.getPageSizeVal()).thenReturn(20L);
|
||||
|
||||
BackupOfferingVO parentDomainOffering = createMockOffering(1L, "Parent Domain Offering");
|
||||
BackupOfferingVO siblingDomainOffering = createMockOffering(2L, "Sibling Domain Offering");
|
||||
|
||||
List<BackupOfferingVO> allOfferings = List.of(parentDomainOffering, siblingDomainOffering);
|
||||
|
||||
SearchBuilder<BackupOfferingVO> sb = Mockito.mock(SearchBuilder.class);
|
||||
SearchCriteria<BackupOfferingVO> sc = Mockito.mock(SearchCriteria.class);
|
||||
BackupOfferingVO entityMock = Mockito.mock(BackupOfferingVO.class);
|
||||
when(backupOfferingDao.createSearchBuilder()).thenReturn(sb);
|
||||
when(sb.entity()).thenReturn(entityMock);
|
||||
when(sb.and(Mockito.anyString(), Mockito.any(), Mockito.any(SearchCriteria.Op.class))).thenReturn(sb);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(backupOfferingDao.searchAndCount(Mockito.any(), Mockito.any()))
|
||||
.thenReturn(new Pair<>(allOfferings, allOfferings.size()));
|
||||
|
||||
when(backupOfferingDetailsDao.findDomainIds(1L)).thenReturn(List.of(parentDomainId));
|
||||
when(backupOfferingDetailsDao.findDomainIds(2L)).thenReturn(List.of(4L));
|
||||
|
||||
when(domainDao.isChildDomain(parentDomainId, childDomainId)).thenReturn(true);
|
||||
when(domainDao.isChildDomain(4L, childDomainId)).thenReturn(false);
|
||||
|
||||
Account account = Mockito.mock(Account.class);
|
||||
when(account.getType()).thenReturn(Account.Type.NORMAL);
|
||||
|
||||
try (MockedStatic<CallContext> mockedCallContext = Mockito.mockStatic(CallContext.class)) {
|
||||
CallContext contextMock = Mockito.mock(CallContext.class);
|
||||
mockedCallContext.when(CallContext::current).thenReturn(contextMock);
|
||||
when(contextMock.getCallingAccount()).thenReturn(account);
|
||||
|
||||
Pair<List<BackupOffering>, Integer> result = backupManager.listBackupOfferings(cmd);
|
||||
|
||||
assertEquals(1, result.first().size());
|
||||
assertEquals("Parent Domain Offering", result.first().get(0).getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListBackupOfferingsWithDomainFilteringExcludesSiblingDomainOfferings() {
|
||||
Long requestedDomainId = 3L;
|
||||
Long siblingDomainId = 4L;
|
||||
|
||||
ListBackupOfferingsCmd cmd =
|
||||
Mockito.mock(ListBackupOfferingsCmd.class);
|
||||
when(cmd.getOfferingId()).thenReturn(null);
|
||||
when(cmd.getDomainId()).thenReturn(requestedDomainId);
|
||||
when(cmd.getStartIndex()).thenReturn(0L);
|
||||
when(cmd.getPageSizeVal()).thenReturn(20L);
|
||||
|
||||
BackupOfferingVO siblingOffering = createMockOffering(1L, "Sibling Domain Offering");
|
||||
List<BackupOfferingVO> allOfferings = List.of(siblingOffering);
|
||||
|
||||
SearchBuilder<BackupOfferingVO> sb = Mockito.mock(SearchBuilder.class);
|
||||
SearchCriteria<BackupOfferingVO> sc = Mockito.mock(SearchCriteria.class);
|
||||
BackupOfferingVO entityMock = Mockito.mock(BackupOfferingVO.class);
|
||||
when(backupOfferingDao.createSearchBuilder()).thenReturn(sb);
|
||||
when(sb.entity()).thenReturn(entityMock);
|
||||
when(sb.and(Mockito.anyString(), Mockito.any(), Mockito.any(SearchCriteria.Op.class))).thenReturn(sb);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(backupOfferingDao.searchAndCount(Mockito.any(), Mockito.any()))
|
||||
.thenReturn(new Pair<>(allOfferings, allOfferings.size()));
|
||||
|
||||
when(backupOfferingDetailsDao.findDomainIds(1L)).thenReturn(List.of(siblingDomainId));
|
||||
when(domainDao.isChildDomain(siblingDomainId, requestedDomainId)).thenReturn(false);
|
||||
|
||||
Account account = Mockito.mock(Account.class);
|
||||
when(account.getType()).thenReturn(Account.Type.NORMAL);
|
||||
|
||||
try (MockedStatic<CallContext> mockedCallContext = Mockito.mockStatic(CallContext.class)) {
|
||||
CallContext contextMock = Mockito.mock(CallContext.class);
|
||||
mockedCallContext.when(CallContext::current).thenReturn(contextMock);
|
||||
when(contextMock.getCallingAccount()).thenReturn(account);
|
||||
|
||||
Pair<List<BackupOffering>, Integer> result = backupManager.listBackupOfferings(cmd);
|
||||
|
||||
assertEquals(0, result.first().size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListBackupOfferingsWithDomainFilteringMultipleDomainMappings() {
|
||||
Long requestedDomainId = 5L;
|
||||
Long parentDomainId1 = 1L;
|
||||
Long parentDomainId2 = 2L;
|
||||
Long unrelatedDomainId = 8L;
|
||||
|
||||
ListBackupOfferingsCmd cmd =
|
||||
Mockito.mock(ListBackupOfferingsCmd.class);
|
||||
when(cmd.getOfferingId()).thenReturn(null);
|
||||
when(cmd.getDomainId()).thenReturn(requestedDomainId);
|
||||
when(cmd.getStartIndex()).thenReturn(0L);
|
||||
when(cmd.getPageSizeVal()).thenReturn(20L);
|
||||
|
||||
BackupOfferingVO multiDomainOffering = createMockOffering(1L, "Multi-Domain Offering");
|
||||
List<BackupOfferingVO> allOfferings = List.of(multiDomainOffering);
|
||||
|
||||
SearchBuilder<BackupOfferingVO> sb = Mockito.mock(SearchBuilder.class);
|
||||
SearchCriteria<BackupOfferingVO> sc = Mockito.mock(SearchCriteria.class);
|
||||
BackupOfferingVO entityMock = Mockito.mock(BackupOfferingVO.class);
|
||||
when(backupOfferingDao.createSearchBuilder()).thenReturn(sb);
|
||||
when(sb.entity()).thenReturn(entityMock);
|
||||
when(sb.and(Mockito.anyString(), Mockito.any(), Mockito.any(SearchCriteria.Op.class))).thenReturn(sb);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(backupOfferingDao.searchAndCount(Mockito.any(), Mockito.any()))
|
||||
.thenReturn(new Pair<>(allOfferings, allOfferings.size()));
|
||||
|
||||
when(backupOfferingDetailsDao.findDomainIds(1L))
|
||||
.thenReturn(List.of(parentDomainId1, unrelatedDomainId, parentDomainId2));
|
||||
|
||||
when(domainDao.isChildDomain(parentDomainId1, requestedDomainId)).thenReturn(false);
|
||||
when(domainDao.isChildDomain(unrelatedDomainId, requestedDomainId)).thenReturn(false);
|
||||
when(domainDao.isChildDomain(parentDomainId2, requestedDomainId)).thenReturn(true);
|
||||
|
||||
Account account = Mockito.mock(Account.class);
|
||||
when(account.getType()).thenReturn(Account.Type.NORMAL);
|
||||
|
||||
try (MockedStatic<CallContext> mockedCallContext = Mockito.mockStatic(CallContext.class)) {
|
||||
CallContext contextMock = Mockito.mock(CallContext.class);
|
||||
mockedCallContext.when(CallContext::current).thenReturn(contextMock);
|
||||
when(contextMock.getCallingAccount()).thenReturn(account);
|
||||
|
||||
Pair<List<BackupOffering>, Integer> result = backupManager.listBackupOfferings(cmd);
|
||||
|
||||
assertEquals(1, result.first().size());
|
||||
assertEquals("Multi-Domain Offering", result.first().get(0).getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListBackupOfferingsNormalUserDefaultsToDomainFiltering() {
|
||||
Long userDomainId = 7L;
|
||||
|
||||
ListBackupOfferingsCmd cmd =
|
||||
Mockito.mock(ListBackupOfferingsCmd.class);
|
||||
when(cmd.getOfferingId()).thenReturn(null);
|
||||
when(cmd.getDomainId()).thenReturn(null); // User didn't pass domain filter
|
||||
when(cmd.getStartIndex()).thenReturn(0L);
|
||||
when(cmd.getPageSizeVal()).thenReturn(20L);
|
||||
|
||||
BackupOfferingVO globalOffering = createMockOffering(1L, "Global Offering");
|
||||
BackupOfferingVO userDomainOffering = createMockOffering(2L, "User Domain Offering");
|
||||
BackupOfferingVO otherDomainOffering = createMockOffering(3L, "Other Domain Offering");
|
||||
|
||||
List<BackupOfferingVO> allOfferings = List.of(globalOffering, userDomainOffering, otherDomainOffering);
|
||||
|
||||
SearchBuilder<BackupOfferingVO> sb = Mockito.mock(SearchBuilder.class);
|
||||
SearchCriteria<BackupOfferingVO> sc = Mockito.mock(SearchCriteria.class);
|
||||
BackupOfferingVO entityMock = Mockito.mock(BackupOfferingVO.class);
|
||||
when(backupOfferingDao.createSearchBuilder()).thenReturn(sb);
|
||||
when(sb.entity()).thenReturn(entityMock);
|
||||
when(sb.and(Mockito.anyString(), Mockito.any(), Mockito.any(SearchCriteria.Op.class))).thenReturn(sb);
|
||||
when(sb.create()).thenReturn(sc);
|
||||
when(backupOfferingDao.searchAndCount(Mockito.any(), Mockito.any()))
|
||||
.thenReturn(new Pair<>(allOfferings, allOfferings.size()));
|
||||
|
||||
when(backupOfferingDetailsDao.findDomainIds(1L)).thenReturn(Collections.emptyList()); // Global
|
||||
when(backupOfferingDetailsDao.findDomainIds(2L)).thenReturn(List.of(userDomainId)); // User's domain
|
||||
when(backupOfferingDetailsDao.findDomainIds(3L)).thenReturn(List.of(99L)); // Other domain
|
||||
|
||||
when(domainDao.isChildDomain(99L, userDomainId)).thenReturn(false);
|
||||
|
||||
Account account = Mockito.mock(Account.class);
|
||||
when(account.getType()).thenReturn(Account.Type.NORMAL);
|
||||
when(account.getDomainId()).thenReturn(userDomainId);
|
||||
|
||||
try (MockedStatic<CallContext> mockedCallContext = Mockito.mockStatic(CallContext.class)) {
|
||||
CallContext contextMock = Mockito.mock(CallContext.class);
|
||||
mockedCallContext.when(CallContext::current).thenReturn(contextMock);
|
||||
when(contextMock.getCallingAccount()).thenReturn(account);
|
||||
|
||||
Pair<List<BackupOffering>, Integer> result = backupManager.listBackupOfferings(cmd);
|
||||
|
||||
assertEquals(2, result.first().size());
|
||||
assertTrue(result.first().stream().anyMatch(o -> o.getName().equals("Global Offering")));
|
||||
assertTrue(result.first().stream().anyMatch(o -> o.getName().equals("User Domain Offering")));
|
||||
}
|
||||
}
|
||||
|
||||
private BackupOfferingVO createMockOffering(Long id, String name) {
|
||||
BackupOfferingVO offering = Mockito.mock(BackupOfferingVO.class);
|
||||
when(offering.getId()).thenReturn(id);
|
||||
when(offering.getName()).thenReturn(name);
|
||||
return offering;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import java.util.Set;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.utils.DomainHelper;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
|
|
@ -98,6 +99,9 @@ public class CreateNetworkOfferingTest extends TestCase {
|
|||
@Mock
|
||||
LoadBalancerVMMapDao _loadBalancerVMMapDao;
|
||||
|
||||
@Mock
|
||||
DomainHelper domainHelper;
|
||||
|
||||
@Mock
|
||||
AnnotationDao annotationDao;
|
||||
@Inject
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ except ImportError:
|
|||
raise RuntimeError("python setuptools is required to build Marvin")
|
||||
|
||||
|
||||
VERSION = "4.23.0.0-SNAPSHOT"
|
||||
VERSION = "4.23.0.0"
|
||||
|
||||
setup(name="Marvin",
|
||||
version=VERSION,
|
||||
|
|
|
|||
|
|
@ -340,9 +340,9 @@ export default {
|
|||
icon: 'cloud-upload-outlined',
|
||||
docHelp: 'adminguide/virtual_machines.html#backup-offerings',
|
||||
permission: ['listBackupOfferings'],
|
||||
searchFilters: ['zoneid'],
|
||||
columns: ['name', 'description', 'zonename'],
|
||||
details: ['name', 'id', 'description', 'externalid', 'zone', 'allowuserdrivenbackups', 'created'],
|
||||
searchFilters: ['zoneid', 'domainid'],
|
||||
columns: ['name', 'description', 'domain', 'zonename'],
|
||||
details: ['name', 'id', 'description', 'externalid', 'domain', 'zone', 'allowuserdrivenbackups', 'created'],
|
||||
related: [{
|
||||
name: 'vm',
|
||||
title: 'label.instances',
|
||||
|
|
|
|||
|
|
@ -85,6 +85,33 @@
|
|||
</template>
|
||||
<a-switch v-model:checked="form.allowuserdrivenbackups"/>
|
||||
</a-form-item>
|
||||
<a-form-item name="ispublic" ref="ispublic" :label="$t('label.ispublic')" v-if="isAdmin()">
|
||||
<a-switch v-model:checked="form.ispublic" />
|
||||
</a-form-item>
|
||||
<a-form-item name="domainid" ref="domainid" v-if="!form.ispublic">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.domainid')" :tooltip="apiParams.domainid.description"/>
|
||||
</template>
|
||||
<a-select
|
||||
mode="multiple"
|
||||
:getPopupContainer="(trigger) => trigger.parentNode"
|
||||
v-model:value="form.domainid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="domains.loading"
|
||||
:placeholder="apiParams.domainid.description">
|
||||
<a-select-option v-for="(opt, optIndex) in domains.opts" :key="optIndex" :label="opt.path || opt.name || opt.description">
|
||||
<span>
|
||||
<resource-icon v-if="opt && opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
|
||||
<block-outlined v-else style="margin-right: 5px" />
|
||||
{{ opt.path || opt.name || opt.description }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<div :span="24" class="action-button">
|
||||
<a-button :loading="loading" @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
|
||||
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ this.$t('label.ok') }}</a-button>
|
||||
|
|
@ -96,6 +123,7 @@
|
|||
<script>
|
||||
import { ref, reactive, toRaw } from 'vue'
|
||||
import { getAPI, postAPI } from '@/api'
|
||||
import { isAdmin } from '@/role'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
|
||||
|
|
@ -108,6 +136,10 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
domains: {
|
||||
loading: false,
|
||||
opts: []
|
||||
},
|
||||
zones: {
|
||||
loading: false,
|
||||
opts: []
|
||||
|
|
@ -129,17 +161,23 @@ export default {
|
|||
initForm () {
|
||||
this.formRef = ref()
|
||||
this.form = reactive({
|
||||
allowuserdrivenbackups: true
|
||||
allowuserdrivenbackups: true,
|
||||
ispublic: true
|
||||
})
|
||||
this.rules = reactive({
|
||||
name: [{ required: true, message: this.$t('message.error.required.input') }],
|
||||
description: [{ required: true, message: this.$t('message.error.required.input') }],
|
||||
zoneid: [{ required: true, message: this.$t('message.error.select') }],
|
||||
externalid: [{ required: true, message: this.$t('message.error.select') }]
|
||||
externalid: [{ required: true, message: this.$t('message.error.select') }],
|
||||
domainid: [{ type: 'array', message: this.$t('message.error.select') }]
|
||||
})
|
||||
},
|
||||
isAdmin () {
|
||||
return isAdmin()
|
||||
},
|
||||
fetchData () {
|
||||
this.fetchZone()
|
||||
this.fetchDomainData()
|
||||
},
|
||||
fetchZone () {
|
||||
this.zones.loading = true
|
||||
|
|
@ -151,6 +189,19 @@ export default {
|
|||
this.zones.loading = false
|
||||
})
|
||||
},
|
||||
fetchDomainData () {
|
||||
const params = {}
|
||||
params.listAll = true
|
||||
params.details = 'min'
|
||||
this.domains.loading = true
|
||||
getAPI('listDomains', params).then(json => {
|
||||
this.domains.opts = json.listdomainsresponse.domain || []
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.domains.loading = false
|
||||
})
|
||||
},
|
||||
fetchExternal (zoneId) {
|
||||
if (!zoneId) {
|
||||
this.externals.opts = []
|
||||
|
|
@ -179,6 +230,20 @@ export default {
|
|||
params[key] = input
|
||||
}
|
||||
}
|
||||
if (values.ispublic !== true) {
|
||||
var domainIndexes = values.domainid
|
||||
var domainId = null
|
||||
if (domainIndexes && domainIndexes.length > 0) {
|
||||
var domainIds = []
|
||||
for (var i = 0; i < domainIndexes.length; i++) {
|
||||
domainIds = domainIds.concat(this.domains.opts[domainIndexes[i]].id)
|
||||
}
|
||||
domainId = domainIds.join(',')
|
||||
}
|
||||
if (domainId) {
|
||||
params.domainid = domainId
|
||||
}
|
||||
}
|
||||
params.allowuserdrivenbackups = values.allowuserdrivenbackups
|
||||
this.loading = true
|
||||
const title = this.$t('label.import.offering')
|
||||
|
|
|
|||
Loading…
Reference in New Issue