Compare commits

...

39 Commits

Author SHA1 Message Date
Shawn Edwards 47d46a640b
Merge 7f8b90f52f into 420bf6dff8 2026-01-22 09:17:16 +01:00
Suresh Kumar Anaparti 420bf6dff8
Merge branch '4.22' 2026-01-22 13:24:08 +05:30
Suresh Kumar Anaparti b1f870ae83
Merge branch '4.20' into 4.22 2026-01-22 13:23:21 +05:30
Wei Zhou 036489b288
CKS: fix resource limitation check on cpu when scale cks cluster (#12379) 2026-01-21 09:59:21 +01:00
dependabot[bot] 1b0a036a5b
Bump ads.version from 2.0.0.AM25 to 2.0.0.AM27 (#12269)
Bumps `ads.version` from 2.0.0.AM25 to 2.0.0.AM27.

Updates `org.apache.directory.server:apacheds-server-integ` from 2.0.0.AM25 to 2.0.0.AM27
- [Commits](https://github.com/apache/directory-server/compare/2.0.0.AM25...2.0.0.AM27)

Updates `org.apache.directory.server:apacheds-core-constants` from 2.0.0.AM25 to 2.0.0.AM27
- [Commits](https://github.com/apache/directory-server/compare/2.0.0.AM25...2.0.0.AM27)

Updates `org.apache.directory.server:apacheds-core-annotations` from 2.0.0.AM25 to 2.0.0.AM27
- [Commits](https://github.com/apache/directory-server/compare/2.0.0.AM25...2.0.0.AM27)

Updates `org.apache.directory.server:apacheds-core` from 2.0.0.AM25 to 2.0.0.AM27
- [Commits](https://github.com/apache/directory-server/compare/2.0.0.AM25...2.0.0.AM27)

Updates `org.apache.directory.server:apacheds-protocol-ldap` from 2.0.0.AM25 to 2.0.0.AM27
- [Commits](https://github.com/apache/directory-server/compare/2.0.0.AM25...2.0.0.AM27)

Updates `org.apache.directory.server:apacheds-jdbm-partition` from 2.0.0.AM25 to 2.0.0.AM27
- [Commits](https://github.com/apache/directory-server/compare/2.0.0.AM25...2.0.0.AM27)

Updates `org.apache.directory.server:apacheds-ldif-partition` from 2.0.0.AM25 to 2.0.0.AM27
- [Commits](https://github.com/apache/directory-server/compare/2.0.0.AM25...2.0.0.AM27)

---
updated-dependencies:
- dependency-name: org.apache.directory.server:apacheds-server-integ
  dependency-version: 2.0.0.AM27
  dependency-type: direct:development
  update-type: version-update:semver-patch
- dependency-name: org.apache.directory.server:apacheds-core-constants
  dependency-version: 2.0.0.AM27
  dependency-type: direct:development
  update-type: version-update:semver-patch
- dependency-name: org.apache.directory.server:apacheds-core-annotations
  dependency-version: 2.0.0.AM27
  dependency-type: direct:development
  update-type: version-update:semver-patch
- dependency-name: org.apache.directory.server:apacheds-core
  dependency-version: 2.0.0.AM27
  dependency-type: direct:development
  update-type: version-update:semver-patch
- dependency-name: org.apache.directory.server:apacheds-protocol-ldap
  dependency-version: 2.0.0.AM27
  dependency-type: direct:development
  update-type: version-update:semver-patch
- dependency-name: org.apache.directory.server:apacheds-jdbm-partition
  dependency-version: 2.0.0.AM27
  dependency-type: direct:development
  update-type: version-update:semver-patch
- dependency-name: org.apache.directory.server:apacheds-ldif-partition
  dependency-version: 2.0.0.AM27
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-21 12:07:23 +05:30
dependabot[bot] e2f4ed9dcf
Bump commons-codec:commons-codec from 1.15 to 1.20.0 (#12303)
Bumps [commons-codec:commons-codec](https://github.com/apache/commons-codec) from 1.15 to 1.20.0.
- [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt)
- [Commits](https://github.com/apache/commons-codec/compare/rel/commons-codec-1.15...rel/commons-codec-1.20.0)

---
updated-dependencies:
- dependency-name: commons-codec:commons-codec
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-21 12:06:44 +05:30
Suresh Kumar Anaparti 8db7cab7ba
Storage pool monitor disconnect improvements (#12398) 2026-01-20 09:08:39 +01:00
Nicolas Vazquez 496bc0329c
Fix: Condition for aborting migration, resume paused VMs on destination (#12331) 2026-01-20 08:56:32 +01:00
Abhisar Sinha cf36fb0000
Set nfsVersion in ssvm agent.properties only if it is not null (#12445) 2026-01-20 08:25:16 +01:00
Daman Arora da518e9036
CKS: Add image store validation for Kubernetes version registration (#12418)
Co-authored-by: Daman Arora <daman.arora@shapeblue.com>
2026-01-20 08:13:15 +01:00
Henrique Sato 03d24ff851
Fix NPE on primary storage delete (#11817) 2026-01-20 08:12:16 +01:00
Vitor Hugo Homem Marzarotto 2a6ce0c8a8
Adds url kubernetes iso (#10862)
Co-authored-by: Vitor Hugo Homem Marzarotto <vitor.marzarotto@scclouds.com.br>
Co-authored-by: Henrique Sato <henriquesato2003@gmail.com>
2026-01-20 08:10:42 +01:00
Suresh Kumar Anaparti 04b58acdd6
Merge branch '4.22' 2026-01-19 19:50:51 +05:30
Suresh Kumar Anaparti 3828a3b744
Merge branch '4.20' into 4.22 2026-01-19 19:50:10 +05:30
Manoj Kumar 42f1e19362
Mask vncPasswd being logged in agent.log (#12404) 2026-01-19 14:20:18 +01:00
Pearl Dsilva 8b2f1f19c2
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
2026-01-19 14:21:47 +05:30
Abhishek Kumar a4b1a27c7d
ui: fix 404 on login after forgot password (#12448)
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
2026-01-19 08:50:07 +01:00
Abhisar Sinha 002d9768b2
Add settings to mark cryptographic algorithms in vpn customer gateways as excluded or obsolete (#12193)
This PR introduces several configuration settings using which an operator can mark certain cryptographic algorithms and parameters as excluded or obsolete for VPN Customer Gateway creation for Site-to-Site VPN.

Cloud providers following modern security frameworks (e.g., ISO 27001/27017) are required to enforce and communicate approved cryptographic standards. CloudStack currently accepts several weak or deprecated algorithms without guidance to users. This PR closes that gap by giving operators explicit control over what is disallowed vs discouraged, improving security posture without breaking existing deployments.

These settings are:

1. vpn.customer.gateway.excluded.encryption.algorithms
2. vpn.customer.gateway.excluded.hashing.algorithms
3. vpn.customer.gateway.excluded.ike.versions
4. vpn.customer.gateway.excluded.dh.group
5. vpn.customer.gateway.obsolete.encryption.algorithms
6. vpn.customer.gateway.obsolete.hashing.algorithms
7. vpn.customer.gateway.obsolete.ike.versions
8. vpn.customer.gateway.obsolete.dh.group
2026-01-19 13:18:37 +05:30
Abhishek Kumar 76e6de7f90
ui: fix form data double fetch/reset form data by ownership selection (#11705)
* ui: fix form data double fetch/reset form data by ownership selection

Fixes #10832
2026-01-16 10:48:57 -05:00
Wei Zhou b31c2f4cae
Revert "Review comment on pull request #12436"
This reverts commit a566af35f5.
2026-01-15 19:17:12 +01:00
Wei Zhou a566af35f5 Review comment on pull request #12436 2026-01-15 19:14:51 +01:00
Suresh Kumar Anaparti f1f779a08d
Cleanup snapshot files in datastores for Error-ed snapshots, and some code improvements (#12347) 2026-01-15 12:12:32 +01:00
Nicolas Vazquez aba3285c3c
[Usage] Include accounts uuid on the cloud_usage accounts initial saving (#12420) 2026-01-15 06:50:35 -03:00
Suresh Kumar Anaparti 507c4cd128
Merge branch '4.22' 2026-01-15 10:09:09 +05:30
Suresh Kumar Anaparti b5fd39fca0
Merge branch '4.20' into 4.22 2026-01-15 10:08:27 +05:30
Abhisar Sinha 5c1f9315ce
Remove transaction from updateCapacityForHost cal (#12421) 2026-01-14 16:12:42 +01:00
Suresh Kumar Anaparti 6a324da27a
MAC address assignment improvements (#12349) 2026-01-13 13:56:39 +01:00
Vishesh b8ed34e2a8
Reset modifier button's state (#12187) 2026-01-13 13:48:55 +01:00
Abhisar Sinha 9e86fdf1b6
Fix cloud-sysvmadmin hang (#12355) 2026-01-13 09:06:25 +01:00
Abhishek Kumar 538578366a Merge remote-tracking branch 'apache/4.22' 2026-01-13 11:49:07 +05:30
Abhishek Kumar 031fbf43d4 Merge remote-tracking branch 'apache/4.20' into 4.22 2026-01-13 11:48:05 +05:30
Abhishek Kumar 8627c60b95
ui: option to migrate vm with volumes to same pool (#11703)
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
2026-01-12 14:27:04 +01:00
Rene Peinthor 8dcfc7c767
Linstor fix host picking (#12047) 2026-01-12 11:29:31 +01:00
Suresh Kumar Anaparti 2b373a4659
[UI] Fix primary storage details display when the uuid has divergent pattern (#12307)
* [UI] Fix primary storage details display when the uuid has different pattern (eg. for pools with SolidFireShared provider)

* Fix on refresh

---------

Co-authored-by: vishesh92 <vishesh92@gmail.com>
2026-01-12 14:18:35 +05:30
Abhisar Sinha c7cfeb5caa
fix location constraint ceph error (#12285) 2026-01-12 09:13:12 +01:00
Abhishek Kumar 0e6d2d986b
ui: prevent calling listConfigurations when not allowed (#11704)
By default, normal users won't have access to listConfigurations API,
therefore, UI should not call it when access is not there.

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
2026-01-12 13:23:37 +05:30
Suresh Kumar Anaparti db1c7d678c
Updated protobuf version to 3.25.5, and protobuf & jackson maven dependencies (#12389) 2026-01-12 08:21:19 +01:00
Suresh Kumar Anaparti 2399edd380
[UI] Fix for the login url with nested redirect parameters (#12356) 2026-01-12 12:11:45 +05:30
Nicolas Vazquez 04875f1517
Improve logs for VM migrations (#12332) 2026-01-09 17:50:27 +01:00
137 changed files with 4697 additions and 1111 deletions

View File

@ -503,6 +503,7 @@ public class EventTypes {
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE = "VPN.S2S.CUSTOMER.GATEWAY.CREATE";
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE = "VPN.S2S.CUSTOMER.GATEWAY.DELETE";
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE = "VPN.S2S.CUSTOMER.GATEWAY.UPDATE";
public static final String EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS = "VPN.S2S.GATEWAY.OBSOLETE.PARAMS";
public static final String EVENT_S2S_VPN_CONNECTION_CREATE = "VPN.S2S.CONNECTION.CREATE";
public static final String EVENT_S2S_VPN_CONNECTION_DELETE = "VPN.S2S.CONNECTION.DELETE";
public static final String EVENT_S2S_VPN_CONNECTION_RESET = "VPN.S2S.CONNECTION.RESET";
@ -1151,6 +1152,7 @@ public class EventTypes {
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_CREATE, Site2SiteVpnConnection.class);
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_DELETE, Site2SiteVpnConnection.class);
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_RESET, Site2SiteVpnConnection.class);

View File

@ -125,6 +125,10 @@ public interface NetworkModel {
*/
String getNextAvailableMacAddressInNetwork(long networkConfigurationId) throws InsufficientAddressCapacityException;
String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException;
boolean isMACUnique(String mac, long networkId);
PublicIpAddress getPublicIpAddress(long ipAddressId);
List<? extends Vlan> listPodVlans(long podId);
@ -364,4 +368,8 @@ public interface NetworkModel {
boolean checkSecurityGroupSupportForNetwork(Account account, DataCenter zone, List<Long> networkIds,
List<Long> securityGroupsIds);
default long getMacIdentifier(Long dataCenterId) {
return 0;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -74,6 +74,7 @@ public interface AlertService {
public static final AlertType ALERT_TYPE_VR_PUBLIC_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PUBLIC.IFACE.MTU", true);
public static final AlertType ALERT_TYPE_VR_PRIVATE_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PRIVATE.IFACE.MTU", true);
public static final AlertType ALERT_TYPE_EXTENSION_PATH_NOT_READY = new AlertType((short)33, "ALERT.TYPE.EXTENSION.PATH.NOT.READY", true);
public static final AlertType ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS = new AlertType((short)34, "ALERT.S2S.VPN.GATEWAY.OBSOLETE.PARAMETERS", true);
public static final AlertType ALERT_TYPE_BACKUP_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_BACKUP_STORAGE, "ALERT.STORAGE.BACKUP", true);
public static final AlertType ALERT_TYPE_OBJECT_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_OBJECT_STORAGE, "ALERT.STORAGE.OBJECT", true);

View File

@ -1216,6 +1216,7 @@ public class ApiConstants {
public static final String DOCKER_REGISTRY_EMAIL = "dockerregistryemail";
public static final String ISO_NAME = "isoname";
public static final String ISO_STATE = "isostate";
public static final String ISO_URL = "isourl";
public static final String SEMANTIC_VERSION = "semanticversion";
public static final String KUBERNETES_VERSION_ID = "kubernetesversionid";
public static final String KUBERNETES_VERSION_NAME = "kubernetesversionname";
@ -1364,6 +1365,10 @@ public class ApiConstants {
public static final String RECURSIVE_DOMAINS = "recursivedomains";
public static final String VPN_CUSTOMER_GATEWAY_PARAMETERS = "vpncustomergatewayparameters";
public static final String OBSOLETE_PARAMETERS = "obsoleteparameters";
public static final String EXCLUDED_PARAMETERS = "excludedparameters";
/**
* This enum specifies IO Drivers, each option controls specific policies on I/O.
* Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).

View File

@ -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<>();

View File

@ -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///////////////////
/////////////////////////////////////////////////////

View File

@ -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;

View File

@ -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");
}
/////////////////////////////////////////////////////

View File

@ -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() {

View File

@ -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() {

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -21,7 +21,9 @@ import java.util.Map;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.CapabilitiesResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.config.ApiServiceConfiguration;
import com.cloud.user.Account;
@ -30,12 +32,22 @@ import com.cloud.user.Account;
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class ListCapabilitiesCmd extends BaseCmd {
@Parameter(name = ApiConstants.DOMAIN_ID,
type = CommandType.UUID,
entityType = DomainResponse.class,
description = "the domain for listing capabilities.",
since = "4.23.0")
private Long domainId;
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
public Long getDomainId() {
return domainId;
}
@Override
public void execute() {
Map<String, Object> capabilities = _mgr.listCapabilities(this);
@ -76,6 +88,10 @@ public class ListCapabilitiesCmd extends BaseCmd {
response.setExtensionsPath((String)capabilities.get(ApiConstants.EXTENSIONS_PATH));
response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED));
response.setAdditionalConfigEnabled((Boolean) capabilities.get(ApiConstants.ADDITONAL_CONFIG_ENABLED));
if (capabilities.containsKey(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS)) {
Map<String, Object> vpnCustomerGatewayParameters = (Map<String, Object>) capabilities.get(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS);
response.setVpnCustomerGatewayParameters(vpnCustomerGatewayParameters);
}
response.setObjectName("capability");
response.setResponseName(getCommandName());
this.setResponseObject(response);

View File

@ -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;
}
}

View File

@ -16,6 +16,8 @@
// under the License.
package org.apache.cloudstack.api.response;
import java.util.Map;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
@ -153,6 +155,10 @@ public class CapabilitiesResponse extends BaseResponse {
@Param(description = "true if additional configurations or extraconfig can be passed to Instances", since = "4.20.2")
private Boolean additionalConfigEnabled;
@SerializedName(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS)
@Param(description = "Excluded and obsolete VPN customer gateway cryptographic parameters")
private Map<String, Object> vpnCustomerGatewayParameters;
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
this.securityGroupsEnabled = securityGroupsEnabled;
}
@ -280,4 +286,8 @@ public class CapabilitiesResponse extends BaseResponse {
public void setAdditionalConfigEnabled(Boolean additionalConfigEnabled) {
this.additionalConfigEnabled = additionalConfigEnabled;
}
public void setVpnCustomerGatewayParameters(Map<String, Object> vpnCustomerGatewayParameters) {
this.vpnCustomerGatewayParameters = vpnCustomerGatewayParameters;
}
}

View File

@ -114,6 +114,14 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotation
@Param(description = "Which IKE Version to use, one of ike (autoselect), IKEv1, or IKEv2. Defaults to ike")
private String ikeVersion;
@SerializedName(ApiConstants.OBSOLETE_PARAMETERS)
@Param(description = "Contains the list of obsolete/insecure cryptographic parameters that the vpn customer gateway is using.", since = "4.23.0")
private String obsoleteParameters;
@SerializedName(ApiConstants.EXCLUDED_PARAMETERS)
@Param(description = "Contains the list of excluded/not allowed cryptographic parameters that the vpn customer gateway is using.", since = "4.23.0")
private String excludedParameters;
public void setId(String id) {
this.id = id;
}
@ -202,4 +210,12 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponseWithAnnotation
this.domainPath = domainPath;
}
public void setContainsObsoleteParameters(String obsoleteParameters) {
this.obsoleteParameters = obsoleteParameters;
}
public void setContainsExcludedParameters(String excludedParameters) {
this.excludedParameters = excludedParameters;
}
}

View File

@ -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

View File

@ -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"));
}
}

View File

@ -30,6 +30,8 @@ public interface SnapshotDataFactory {
SnapshotInfo getSnapshot(long snapshotId, long storeId, DataStoreRole role);
SnapshotInfo getSnapshotIncludingRemoved(long snapshotId, long storeId, DataStoreRole role);
SnapshotInfo getSnapshotWithRoleAndZone(long snapshotId, DataStoreRole role, long zoneId);
SnapshotInfo getSnapshotOnPrimaryStore(long snapshotId);

View File

@ -40,7 +40,11 @@ public class PublicIp implements PublicIpAddress {
}
public static PublicIp createFromAddrAndVlan(IPAddressVO addr, VlanVO vlan) {
return new PublicIp(addr, vlan, NetUtils.createSequenceBasedMacAddress(addr.getMacAddress(), NetworkModel.MACIdentifier.value()));
long macIdentifier = NetworkModel.MACIdentifier.valueIn(addr.getDataCenterId());
if (macIdentifier == 0) {
macIdentifier = addr.getDataCenterId();
}
return new PublicIp(addr, vlan, NetUtils.createSequenceBasedMacAddress(addr.getMacAddress(), macIdentifier));
}
@Override
@ -279,5 +283,4 @@ public class PublicIp implements PublicIpAddress {
public boolean isForRouter() {
return _addr.isForRouter();
}
}

View File

@ -3053,7 +3053,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
}
protected void migrate(final VMInstanceVO vm, final long srcHostId, final DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException {
logger.info("Migrating {} to {}", vm, dest);
logger.info("Start preparing migration of the VM: {} to {}", vm, dest);
final long dstHostId = dest.getHost().getId();
final Host fromHost = _hostDao.findById(srcHostId);
if (fromHost == null) {
@ -3118,9 +3118,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
if (pfma == null || !pfma.getResult()) {
final String details = pfma != null ? pfma.getDetails() : "null answer returned";
final String msg = "Unable to prepare for migration due to " + details;
logger.error("Failed to prepare destination host {} for migration of VM {} : {}", dstHostId, vm.getInstanceName(), details);
pfma = null;
throw new AgentUnavailableException(msg, dstHostId);
}
logger.debug("Successfully prepared destination host {} for migration of VM {} ", dstHostId, vm.getInstanceName());
} catch (final OperationTimedoutException e1) {
throw new AgentUnavailableException("Operation timed out", dstHostId);
} finally {
@ -3141,18 +3143,23 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
volumeMgr.release(vm.getId(), dstHostId);
}
logger.info("Migration cancelled because state has changed: {}", vm);
throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm);
String msg = "Migration cancelled because state has changed: " + vm;
logger.warn(msg);
throw new ConcurrentOperationException(msg);
}
} catch (final NoTransitionException e1) {
_networkMgr.rollbackNicForMigration(vmSrc, profile);
volumeMgr.release(vm.getId(), dstHostId);
logger.info("Migration cancelled because {}", e1.getMessage());
String msg = String.format("Migration cancelled for VM %s due to state transition failure: %s",
vm.getInstanceName(), e1.getMessage());
logger.warn(msg, e1);
throw new ConcurrentOperationException("Migration cancelled because " + e1.getMessage());
} catch (final CloudRuntimeException e2) {
_networkMgr.rollbackNicForMigration(vmSrc, profile);
volumeMgr.release(vm.getId(), dstHostId);
logger.info("Migration cancelled because {}", e2.getMessage());
String msg = String.format("Migration cancelled for VM %s due to runtime exception: %s",
vm.getInstanceName(), e2.getMessage());
logger.error(msg, e2);
work.setStep(Step.Done);
_workDao.update(work.getId(), work);
try {
@ -3172,8 +3179,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);
if (ma == null || !ma.getResult()) {
final String details = ma != null ? ma.getDetails() : "null answer returned";
String msg = String.format("Migration command failed for VM %s on source host id=%s to destination host %s: %s",
vm.getInstanceName(), vm.getLastHostId(), dstHostId, details);
logger.error(msg);
throw new CloudRuntimeException(details);
}
logger.info("Migration command successful for VM {}", vm.getInstanceName());
} catch (final OperationTimedoutException e) {
boolean success = false;
if (HypervisorType.KVM.equals(vm.getHypervisorType())) {
@ -3210,7 +3221,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
try {
if (!checkVmOnHost(vm, dstHostId)) {
logger.error("Unable to complete migration for {}", vm);
logger.error("Migration verification failed for VM {} : VM not found on destination host {} ", vm.getInstanceName(), dstHostId);
try {
_agentMgr.send(srcHostId, new Commands(cleanup(vm, dpdkInterfaceMapping)), null);
} catch (final AgentUnavailableException e) {
@ -3225,7 +3236,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
migrated = true;
} finally {
if (!migrated) {
logger.info("Migration was unsuccessful. Cleaning up: {}", vm);
logger.info("Migration was unsuccessful. Cleaning up: {}", vm);
_networkMgr.rollbackNicForMigration(vmSrc, profile);
volumeMgr.release(vm.getId(), dstHostId);
// deallocate GPU devices for the VM on the destination host
@ -3237,7 +3248,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
try {
_agentMgr.send(dstHostId, new Commands(cleanup(vm, dpdkInterfaceMapping)), null);
} catch (final AgentUnavailableException ae) {
logger.warn("Looks like the destination Host is unavailable for cleanup", ae);
logger.warn("Destination host {} unavailable for cleanup after failed migration of VM {}", dstHostId, vm.getInstanceName(), ae);
}
_networkMgr.setHypervisorHostname(profile, dest, false);
try {
@ -3246,6 +3257,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
logger.warn(e.getMessage());
}
} else {
logger.info("Migration completed successfully for VM %s" + vm);
_networkMgr.commitNicForMigration(vmSrc, profile);
volumeMgr.release(vm.getId(), srcHostId);
// deallocate GPU devices for the VM on the src host after migration is complete
@ -3276,6 +3288,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
migrateCommand.setVlanToPersistenceMap(vlanToPersistenceMap);
}
logger.debug("Setting auto convergence to: {}", StorageManager.KvmAutoConvergence.value());
migrateCommand.setAutoConvergence(StorageManager.KvmAutoConvergence.value());
migrateCommand.setHostGuid(destination.getHost().getGuid());

View File

@ -1283,7 +1283,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
nicProfile.setIPv4Gateway(ipv4Gateway);
nicProfile.setIPv4Netmask(ipv4Netmask);
if (nicProfile.getMacAddress() == null) {
if (nicProfile.getMacAddress() == null || !_networkModel.isMACUnique(nicProfile.getMacAddress(), network.getId())) {
try {
String macAddress = _networkModel.getNextAvailableMacAddressInNetwork(network.getId());
nicProfile.setMacAddress(macAddress);

View File

@ -396,6 +396,7 @@ public class NetworkOrchestratorTest extends TestCase {
when(testOrchestrator._ipAddressDao.acquireInLockTable(Mockito.anyLong())).thenReturn(ipVoSpy);
when(testOrchestrator._ipAddressDao.update(Mockito.anyLong(), Mockito.any(IPAddressVO.class))).thenReturn(true);
when(testOrchestrator._ipAddressDao.releaseFromLockTable(Mockito.anyLong())).thenReturn(true);
when(testOrchestrator._networkModel.isMACUnique(Mockito.anyString(), Mockito.anyLong())).thenReturn(true);
try {
when(testOrchestrator._networkModel.getNextAvailableMacAddressInNetwork(Mockito.anyLong())).thenReturn(macAddress);
} catch (InsufficientAddressCapacityException e) {

View File

@ -462,8 +462,8 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
public String getNextAvailableMacAddress(final long networkConfigId, Integer zoneMacIdentifier) {
final SequenceFetcher fetch = SequenceFetcher.getInstance();
long seq = fetch.getNextSequence(Long.class, _tgMacAddress, networkConfigId);
if(zoneMacIdentifier != null && zoneMacIdentifier.intValue() != 0 ){
seq = seq | _prefix << 40 | (long)zoneMacIdentifier << 32 | networkConfigId << 16 & 0x00000000ffff0000l;
if (zoneMacIdentifier != null && zoneMacIdentifier != 0) {
seq = seq | _prefix << 40 | (long)zoneMacIdentifier << 32 | networkConfigId << 16 & 0x00000000ffff0000L;
}
return NetUtils.long2Mac(seq);
}

View File

@ -47,6 +47,8 @@ public interface SnapshotDao extends GenericDao<SnapshotVO, Long>, StateDao<Snap
List<SnapshotVO> listAllByStatus(Snapshot.State... status);
List<SnapshotVO> listAllByStatusIncludingRemoved(Snapshot.State... status);
void updateVolumeIds(long oldVolId, long newVolId);
List<SnapshotVO> listByStatusNotIn(long volumeId, Snapshot.State... status);

View File

@ -265,6 +265,13 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
return listBy(sc, null);
}
@Override
public List<SnapshotVO> listAllByStatusIncludingRemoved(Snapshot.State... status) {
SearchCriteria<SnapshotVO> sc = StatusSearch.create();
sc.setParameters("status", (Object[])status);
return listIncludingRemovedBy(sc, null);
}
@Override
public List<SnapshotVO> listByIds(Object... ids) {
SearchCriteria<SnapshotVO> sc = snapshotIdsSearch.create();

View File

@ -52,7 +52,7 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
private static final String DELETE_ALL = "DELETE FROM cloud_usage";
private static final String DELETE_ALL_BY_ACCOUNTID = "DELETE FROM cloud_usage WHERE account_id = ?";
private static final String DELETE_ALL_BY_INTERVAL = "DELETE FROM cloud_usage WHERE end_date < DATE_SUB(CURRENT_DATE(), INTERVAL ? DAY)";
private static final String INSERT_ACCOUNT = "INSERT INTO cloud_usage.account (id, account_name, type, role_id, domain_id, removed, cleanup_needed) VALUES (?,?,?,?,?,?,?)";
private static final String INSERT_ACCOUNT = "INSERT INTO cloud_usage.account (id, account_name, uuid, type, role_id, domain_id, removed, cleanup_needed) VALUES (?,?,?,?,?,?,?,?)";
private static final String INSERT_USER_STATS = "INSERT INTO cloud_usage.user_statistics (id, data_center_id, account_id, public_ip_address, device_id, device_type, network_id, net_bytes_received,"
+ " net_bytes_sent, current_bytes_received, current_bytes_sent, agg_bytes_received, agg_bytes_sent) VALUES (?,?,?,?,?,?,?,?,?,?, ?, ?, ?)";
@ -129,25 +129,26 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
for (AccountVO acct : accounts) {
pstmt.setLong(1, acct.getId());
pstmt.setString(2, acct.getAccountName());
pstmt.setInt(3, acct.getType().ordinal());
pstmt.setString(3, acct.getUuid());
pstmt.setInt(4, acct.getType().ordinal());
//prevent autoboxing NPE by defaulting to User role
if(acct.getRoleId() == null){
pstmt.setLong(4, RoleType.User.getId());
pstmt.setLong(5, RoleType.User.getId());
}else{
pstmt.setLong(4, acct.getRoleId());
pstmt.setLong(5, acct.getRoleId());
}
pstmt.setLong(5, acct.getDomainId());
pstmt.setLong(6, acct.getDomainId());
Date removed = acct.getRemoved();
if (removed == null) {
pstmt.setString(6, null);
pstmt.setString(7, null);
} else {
pstmt.setString(6, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), acct.getRemoved()));
pstmt.setString(7, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), acct.getRemoved()));
}
pstmt.setBoolean(7, acct.getNeedsCleanup());
pstmt.setBoolean(8, acct.getNeedsCleanup());
pstmt.addBatch();
}

View File

@ -95,7 +95,7 @@ public interface NicDao extends GenericDao<NicVO, Long> {
List<NicVO> listByVmIdAndKeyword(long instanceId, String keyword);
NicVO findByMacAddress(String macAddress);
NicVO findByMacAddress(String macAddress, long networkId);
NicVO findByNetworkIdAndMacAddressIncludingRemoved(long networkId, String mac);

View File

@ -420,9 +420,10 @@ public class NicDaoImpl extends GenericDaoBase<NicVO, Long> implements NicDao {
}
@Override
public NicVO findByMacAddress(String macAddress) {
public NicVO findByMacAddress(String macAddress, long networkId) {
SearchCriteria<NicVO> sc = AllFieldsSearch.create();
sc.setParameters("macAddress", macAddress);
sc.setParameters("network", networkId);
return findOneBy(sc);
}

View File

@ -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;
}
}

View File

@ -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"));
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}
}
}

View File

@ -76,6 +76,8 @@ StateDao<ObjectInDataStoreStateMachine.State, ObjectInDataStoreStateMachine.Even
List<SnapshotDataStoreVO> findBySnapshotId(long snapshotId);
List<SnapshotDataStoreVO> findBySnapshotIdWithNonDestroyedState(long snapshotId);
void duplicateCacheRecordsOnRegionStore(long storeId);
// delete the snapshot entry on primary data store to make sure that next snapshot will be full snapshot

View File

@ -464,6 +464,13 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
@Override
public List<SnapshotDataStoreVO> findBySnapshotId(long snapshotId) {
SearchCriteria<SnapshotDataStoreVO> sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create();
sc.setParameters(SNAPSHOT_ID, snapshotId);
return listBy(sc);
}
@Override
public List<SnapshotDataStoreVO> findBySnapshotIdWithNonDestroyedState(long snapshotId) {
SearchCriteria<SnapshotDataStoreVO> sc = idStateNinSearch.create();
sc.setParameters(SNAPSHOT_ID, snapshotId);
sc.setParameters(STATE, State.Destroyed.name());

View File

@ -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>

View File

@ -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';

View File

@ -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);
}
}

View File

@ -303,7 +303,7 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
}
if (Snapshot.State.Error.equals(snapshotVO.getState())) {
List<SnapshotDataStoreVO> storeRefs = snapshotStoreDao.findBySnapshotId(snapshotId);
List<SnapshotDataStoreVO> storeRefs = snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
List<Long> deletedRefs = new ArrayList<>();
for (SnapshotDataStoreVO ref : storeRefs) {
boolean refZoneIdMatch = false;

View File

@ -94,7 +94,7 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory {
if (snapshot == null) { //snapshot may have been removed;
return new ArrayList<>();
}
List<SnapshotDataStoreVO> allSnapshotsAndDataStore = snapshotStoreDao.findBySnapshotId(snapshotId);
List<SnapshotDataStoreVO> allSnapshotsAndDataStore = snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
if (CollectionUtils.isEmpty(allSnapshotsAndDataStore)) {
return new ArrayList<>();
}
@ -118,7 +118,23 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory {
if (snapshot == null) {
return null;
}
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(role, storeId, snapshotId);
return getSnapshotOnStore(snapshot, storeId, role);
}
@Override
public SnapshotInfo getSnapshotIncludingRemoved(long snapshotId, long storeId, DataStoreRole role) {
SnapshotVO snapshot = snapshotDao.findByIdIncludingRemoved(snapshotId);
if (snapshot == null) {
return null;
}
return getSnapshotOnStore(snapshot, storeId, role);
}
private SnapshotInfo getSnapshotOnStore(SnapshotVO snapshot, long storeId, DataStoreRole role) {
if (snapshot == null) {
return null;
}
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(role, storeId, snapshot.getId());
if (snapshotStore == null) {
return null;
}
@ -207,7 +223,7 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory {
@Override
public void updateOperationFailed(long snapshotId) throws NoTransitionException {
List<SnapshotDataStoreVO> snapshotStoreRefs = snapshotStoreDao.findBySnapshotId(snapshotId);
List<SnapshotDataStoreVO> snapshotStoreRefs = snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
for (SnapshotDataStoreVO snapshotStoreRef : snapshotStoreRefs) {
SnapshotInfo snapshotInfo = getSnapshot(snapshotStoreRef.getSnapshotId(), snapshotStoreRef.getDataStoreId(), snapshotStoreRef.getRole());
if (snapshotInfo != null) {

View File

@ -474,8 +474,7 @@ public class SnapshotServiceImpl implements SnapshotService {
if (res.isFailed()) {
throw new CloudRuntimeException(res.getResult());
}
SnapshotInfo destSnapshot = res.getSnapshot();
return destSnapshot;
return res.getSnapshot();
} catch (InterruptedException e) {
logger.debug("failed copy snapshot", e);
throw new CloudRuntimeException("Failed to copy snapshot", e);
@ -483,7 +482,6 @@ public class SnapshotServiceImpl implements SnapshotService {
logger.debug("Failed to copy snapshot", e);
throw new CloudRuntimeException("Failed to copy snapshot", e);
}
}
protected Void copySnapshotAsyncCallback(AsyncCallbackDispatcher<SnapshotServiceImpl, CopyCommandResult> callback, CopySnapshotContext<CommandResult> context) {
@ -571,7 +569,6 @@ public class SnapshotServiceImpl implements SnapshotService {
}
protected Void deleteSnapshotCallback(AsyncCallbackDispatcher<SnapshotServiceImpl, CommandResult> callback, DeleteSnapshotContext<CommandResult> context) {
CommandResult result = callback.getResult();
AsyncCallFuture<SnapshotResult> future = context.future;
SnapshotInfo snapshot = context.snapshot;
@ -729,7 +726,7 @@ public class SnapshotServiceImpl implements SnapshotService {
if (snapshot != null) {
if (snapshot.getState() != Snapshot.State.BackedUp) {
List<SnapshotDataStoreVO> snapshotDataStoreVOs = _snapshotStoreDao.findBySnapshotId(snapshotId);
List<SnapshotDataStoreVO> snapshotDataStoreVOs = _snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
for (SnapshotDataStoreVO snapshotDataStoreVO : snapshotDataStoreVOs) {
logger.debug("Remove snapshot {}, status {} on snapshot_store_ref table with id: {}", snapshot, snapshotDataStoreVO.getState(), snapshotDataStoreVO.getId());
@ -834,7 +831,6 @@ public class SnapshotServiceImpl implements SnapshotService {
SnapshotObject srcSnapshot = (SnapshotObject)snapshot;
srcSnapshot.processEvent(Event.DestroyRequested);
srcSnapshot.processEvent(Event.OperationSucceeded);
srcSnapshot.processEvent(Snapshot.Event.OperationFailed);
_snapshotDetailsDao.removeDetail(srcSnapshot.getId(), AsyncJob.Constants.MS_ID);
@ -845,7 +841,6 @@ public class SnapshotServiceImpl implements SnapshotService {
}
}
});
}
@Override

View File

@ -541,7 +541,6 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
logger.warn("Failed to clean up snapshot '" + snapshot.getId() + "' on primary storage: " + e.getMessage());
}
}
}
private VMSnapshot takeHypervisorSnapshot(VolumeInfo volumeInfo) {

View File

@ -109,6 +109,9 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager {
stateMachines.addTransition(State.Destroying, Event.DestroyRequested, State.Destroying);
stateMachines.addTransition(State.Destroying, Event.OperationSucceeded, State.Destroyed);
stateMachines.addTransition(State.Destroying, Event.OperationFailed, State.Destroying);
stateMachines.addTransition(State.Destroyed, Event.DestroyRequested, State.Destroyed);
stateMachines.addTransition(State.Destroyed, Event.OperationSucceeded, State.Destroyed);
stateMachines.addTransition(State.Destroyed, Event.OperationFailed, State.Destroyed);
stateMachines.addTransition(State.Failed, Event.DestroyRequested, State.Destroying);
// TODO: further investigate why an extra event is sent when it is
// already Ready for DownloadListener

View File

@ -66,7 +66,7 @@ public class ConfigKeyScheduledExecutionWrapper implements Runnable {
this.unit = unit;
}
protected ConfigKeyScheduledExecutionWrapper(ScheduledExecutorService executorService, Runnable command,
public ConfigKeyScheduledExecutionWrapper(ScheduledExecutorService executorService, Runnable command,
ConfigKey<?> configKey, int enableIntervalSeconds, TimeUnit unit) {
validateArgs(executorService, command, configKey);
this.executorService = executorService;

View File

@ -67,6 +67,31 @@
<artifactId>java-linstor</artifactId>
<version>${cs.java-linstor.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${cs.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${cs.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${cs.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${cs.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>${cs.jackson.version}</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>

View File

@ -163,7 +163,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
final String target = command.getDestinationIp();
xmlDesc = dm.getXMLDesc(xmlFlag);
if (logger.isDebugEnabled()) {
logger.debug(String.format("VM [%s] with XML configuration [%s] will be migrated to host [%s].", vmName, xmlDesc, target));
logger.debug("VM {} with XML configuration {} will be migrated to host {}.", vmName, maskSensitiveInfoInXML(xmlDesc), target);
}
// Limit the VNC password in case the length is greater than 8 characters
@ -178,7 +178,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
logger.debug(String.format("Editing mount path of ISO from %s to %s", oldIsoVolumePath, newIsoVolumePath));
xmlDesc = replaceDiskSourceFile(xmlDesc, newIsoVolumePath, vmName);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Replaced disk mount point [%s] with [%s] in Instance [%s] XML configuration. New XML configuration is [%s].", oldIsoVolumePath, newIsoVolumePath, vmName, xmlDesc));
logger.debug("Replaced disk mount point {} with {} in Instance {} XML configuration. New XML configuration is {}.", oldIsoVolumePath, newIsoVolumePath, vmName, maskSensitiveInfoInXML(xmlDesc));
}
}
@ -209,11 +209,11 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
if (migrateStorage) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Changing VM [%s] volumes during migration to host: [%s].", vmName, target));
logger.debug("Changing VM {} volumes during migration to host: {}.", vmName, target);
}
xmlDesc = replaceStorage(xmlDesc, mapMigrateStorage, migrateStorageManaged);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Changed VM [%s] XML configuration of used storage. New XML configuration is [%s].", vmName, xmlDesc));
logger.debug("Changed VM {} XML configuration of used storage. New XML configuration is {}.", vmName, maskSensitiveInfoInXML(xmlDesc));
}
migrateDiskLabels = getMigrateStorageDeviceLabels(disks, mapMigrateStorage);
}
@ -221,11 +221,11 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
Map<String, DpdkTO> dpdkPortsMapping = command.getDpdkInterfaceMapping();
if (MapUtils.isNotEmpty(dpdkPortsMapping)) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Changing VM [%s] DPDK interfaces during migration to host: [%s].", vmName, target));
logger.trace("Changing VM {} DPDK interfaces during migration to host: {}.", vmName, target);
}
xmlDesc = replaceDpdkInterfaces(xmlDesc, dpdkPortsMapping);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Changed VM [%s] XML configuration of DPDK interfaces. New XML configuration is [%s].", vmName, xmlDesc));
logger.debug("Changed VM {} XML configuration of DPDK interfaces. New XML configuration is {}.", vmName, maskSensitiveInfoInXML(xmlDesc));
}
}
@ -240,7 +240,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
}
//run migration in thread so we can monitor it
logger.info(String.format("Starting live migration of instance [%s] to destination host [%s] having the final XML configuration: [%s].", vmName, dconn.getURI(), xmlDesc));
logger.info("Starting live migration of instance {} to destination host {} having the final XML configuration: {}.", vmName, dconn.getURI(), maskSensitiveInfoInXML(xmlDesc));
final ExecutorService executor = Executors.newFixedThreadPool(1);
boolean migrateNonSharedInc = command.isMigrateNonSharedInc() && !migrateStorageManaged;
@ -256,20 +256,21 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
final Future<Domain> migrateThread = executor.submit(worker);
executor.shutdown();
long sleeptime = 0;
final int migrateDowntime = libvirtComputingResource.getMigrateDowntime();
boolean isMigrateDowntimeSet = false;
while (!executor.isTerminated()) {
Thread.sleep(100);
sleeptime += 100;
if (sleeptime == 1000) { // wait 1s before attempting to set downtime on migration, since I don't know of a VIR_DOMAIN_MIGRATING state
final int migrateDowntime = libvirtComputingResource.getMigrateDowntime();
if (migrateDowntime > 0 ) {
try {
final int setDowntime = dm.migrateSetMaxDowntime(migrateDowntime);
if (setDowntime == 0 ) {
logger.debug("Set max downtime for migration of " + vmName + " to " + String.valueOf(migrateDowntime) + "ms");
}
} catch (final LibvirtException e) {
logger.debug("Failed to set max downtime for migration, perhaps migration completed? Error: " + e.getMessage());
if (!isMigrateDowntimeSet && migrateDowntime > 0 && sleeptime >= 1000) { // wait 1s before attempting to set downtime on migration, since I don't know of a VIR_DOMAIN_MIGRATING state
try {
final int setDowntime = dm.migrateSetMaxDowntime(migrateDowntime);
if (setDowntime == 0 ) {
isMigrateDowntimeSet = true;
logger.debug("Set max downtime for migration of " + vmName + " to " + String.valueOf(migrateDowntime) + "ms");
}
} catch (final LibvirtException e) {
logger.debug("Failed to set max downtime for migration, perhaps migration completed? Error: " + e.getMessage());
}
}
if (sleeptime % 1000 == 0) {
@ -278,17 +279,20 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
// abort the vm migration if the job is executed more than vm.migrate.wait
final int migrateWait = libvirtComputingResource.getMigrateWait();
logger.info("vm.migrate.wait value set to: {}for VM: {}", migrateWait, vmName);
if (migrateWait > 0 && sleeptime > migrateWait * 1000) {
DomainState state = null;
try {
state = dm.getInfo().state;
logger.info("VM domain state when trying to abort migration : {}", state);
} catch (final LibvirtException e) {
logger.info("Couldn't get VM domain state after " + sleeptime + "ms: " + e.getMessage());
}
if (state != null && state == DomainState.VIR_DOMAIN_RUNNING) {
if (state != null && (state == DomainState.VIR_DOMAIN_RUNNING || state == DomainState.VIR_DOMAIN_PAUSED)) {
try {
DomainJobInfo job = dm.getJobInfo();
logger.info(String.format("Aborting migration of VM [%s] with domain job [%s] due to time out after %d seconds.", vmName, job, migrateWait));
logger.warn("Aborting migration of VM {} with domain job [{}] due to timeout after {} seconds. " +
"Job stats: data processed={} bytes, data remaining={} bytes", vmName, job, migrateWait, job.getDataProcessed(), job.getDataRemaining());
dm.abortJob();
result = String.format("Migration of VM [%s] was cancelled by CloudStack due to time out after %d seconds.", vmName, migrateWait);
commandState = Command.State.FAILED;
@ -303,10 +307,12 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
// pause vm if we meet the vm.migrate.pauseafter threshold and not already paused
final int migratePauseAfter = libvirtComputingResource.getMigratePauseAfter();
logger.info("vm.migrate.pauseafter value set to: {} for VM: {}", migratePauseAfter, vmName);
if (migratePauseAfter > 0 && sleeptime > migratePauseAfter) {
DomainState state = null;
try {
state = dm.getInfo().state;
logger.info("VM domain state when trying to pause VM for migration: {}", state);
} catch (final LibvirtException e) {
logger.info("Couldn't get VM domain state after " + sleeptime + "ms: " + e.getMessage());
}
@ -329,6 +335,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
if (logger.isDebugEnabled()) {
logger.debug(String.format("Cleaning the disks of VM [%s] in the source pool after VM migration finished.", vmName));
}
resumeDomainIfPaused(destDomain, vmName);
deleteOrDisconnectDisksOnSourcePool(libvirtComputingResource, migrateDiskInfoList, disks);
libvirtComputingResource.cleanOldSecretsByDiskDef(conn, disks);
}
@ -381,6 +388,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
}
if (result == null) {
logger.info("Post-migration cleanup for VM {}: ", vmName);
libvirtComputingResource.destroyNetworkRulesForVM(conn, vmName);
for (final InterfaceDef iface : ifaces) {
String vlanId = libvirtComputingResource.getVlanIdFromBridgeName(iface.getBrName());
@ -394,6 +402,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
commandState = Command.State.COMPLETED;
libvirtComputingResource.createOrUpdateLogFileForCommand(command, commandState);
} else if (commandState == null) {
logger.error("Migration of VM {} failed with result: {}", vmName, result);
commandState = Command.State.FAILED;
libvirtComputingResource.createOrUpdateLogFileForCommand(command, commandState);
}
@ -401,6 +410,28 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
return new MigrateAnswer(command, result == null, result, null);
}
private DomainState getDestDomainState(Domain destDomain, String vmName) {
DomainState dmState = null;
try {
dmState = destDomain.getInfo().state;
} catch (final LibvirtException e) {
logger.info("Failed to get domain state for VM: " + vmName + " due to: " + e.getMessage());
}
return dmState;
}
private void resumeDomainIfPaused(Domain destDomain, String vmName) {
DomainState dmState = getDestDomainState(destDomain, vmName);
if (dmState == DomainState.VIR_DOMAIN_PAUSED) {
logger.info("Resuming VM " + vmName + " on destination after migration");
try {
destDomain.resume();
} catch (final Exception e) {
logger.error("Failed to resume vm " + vmName + " on destination after migration due to : " + e.getMessage());
}
}
}
/**
* Gets the disk labels (vda, vdb...) of the disks mapped for migration on mapMigrateStorage.
* @param diskDefinitions list of all the disksDefinitions of the VM.
@ -708,9 +739,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
graphElem = graphElem.replaceAll("passwd='([^\\s]+)'", "passwd='" + vncPassword + "'");
}
xmlDesc = xmlDesc.replaceAll(GRAPHICS_ELEM_START + CONTENTS_WILDCARD + GRAPHICS_ELEM_END, graphElem);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Replaced the VNC IP address [%s] with [%s] in VM [%s].", originalGraphElem, graphElem, vmName));
}
logger.debug("Replaced the VNC IP address {} with {} in VM {}.", maskSensitiveInfoInXML(originalGraphElem), maskSensitiveInfoInXML(graphElem), vmName);
}
}
return xmlDesc;
@ -1029,4 +1058,10 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
}
return false;
}
public static String maskSensitiveInfoInXML(String xmlDesc) {
if (xmlDesc == null) return null;
return xmlDesc.replaceAll("(graphics\\s+[^>]*type=['\"]vnc['\"][^>]*passwd=['\"])([^'\"]*)(['\"])",
"$1*****$3");
}
}

View File

@ -56,6 +56,7 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp
final VirtualMachineTO vm = command.getVirtualMachine();
if (command.isRollback()) {
logger.info("Handling rollback for PrepareForMigration of VM {}", vm.getName());
return handleRollback(command, libvirtComputingResource);
}
@ -83,6 +84,7 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp
if (interfaceDef != null && interfaceDef.getNetType() == GuestNetType.VHOSTUSER) {
DpdkTO to = new DpdkTO(interfaceDef.getDpdkOvsPath(), interfaceDef.getDpdkSourcePort(), interfaceDef.getInterfaceMode());
dpdkInterfaceMapping.put(nic.getMac(), to);
logger.debug("Configured DPDK interface for VM {}", vm.getName());
}
}
@ -122,6 +124,7 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp
return new PrepareForMigrationAnswer(command, "failed to connect physical disks to host");
}
logger.info("Successfully prepared destination host for migration of VM {}", vm.getName());
return createPrepareForMigrationAnswer(command, dpdkInterfaceMapping, libvirtComputingResource, vm);
} catch (final LibvirtException | CloudRuntimeException | InternalErrorException | URISyntaxException e) {
if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
@ -157,6 +160,7 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp
KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
VirtualMachineTO vmTO = command.getVirtualMachine();
logger.info("Rolling back PrepareForMigration for VM {}: disconnecting physical disks", vmTO.getName());
if (!storagePoolMgr.disconnectPhysicalDisksViaVmSpec(vmTO)) {
return new PrepareForMigrationAnswer(command, "failed to disconnect physical disks from host");
}

View File

@ -84,8 +84,9 @@ public final class LibvirtStartCommandWrapper extends CommandWrapper<StartComman
}
libvirtComputingResource.createVifs(vmSpec, vm);
logger.debug("starting " + vmName + ": " + vm.toString());
if (logger.isDebugEnabled()) {
logger.debug("Starting {} : {}", vmName, LibvirtMigrateCommandWrapper.maskSensitiveInfoInXML(vm.toString()));
}
String vmInitialSpecification = vm.toString();
String vmFinalSpecification = performXmlTransformHook(vmInitialSpecification, libvirtComputingResource);
libvirtComputingResource.startVM(conn, vmName, vmFinalSpecification);

View File

@ -658,7 +658,7 @@ public class LibvirtMigrateCommandWrapperTest {
@Test
public void testReplaceIpForVNCInDescFile() {
final String targetIp = "192.168.22.21";
final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFileAndNormalizePassword(fullfile, targetIp, null, "");
final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFileAndNormalizePassword(fullfile, targetIp, "vncSecretPwd", "");
assertEquals("transformation does not live up to expectation:\n" + result, targetfile, result);
}
@ -1089,6 +1089,30 @@ public class LibvirtMigrateCommandWrapperTest {
Assert.assertTrue(finalXml.contains(newIsoVolumePath));
}
@Test
public void testMaskVncPwdDomain() {
// Test case 1: Single quotes
String xml1 = "<graphics type='vnc' port='5900' passwd='secret123'/>";
String expected1 = "<graphics type='vnc' port='5900' passwd='*****'/>";
assertEquals(expected1, LibvirtMigrateCommandWrapper.maskSensitiveInfoInXML(xml1));
// Test case 2: Double quotes
String xml2 = "<graphics type=\"vnc\" port=\"5901\" passwd=\"mypassword\"/>";
String expected2 = "<graphics type=\"vnc\" port=\"5901\" passwd=\"*****\"/>";
assertEquals(expected2, LibvirtMigrateCommandWrapper.maskSensitiveInfoInXML(xml2));
// Test case 3: Non-VNC graphics (should remain unchanged)
String xml3 = "<graphics type='spice' port='5902' passwd='notvnc'/>";
assertEquals(xml3, LibvirtMigrateCommandWrapper.maskSensitiveInfoInXML(xml3));
// Test case 4: Multiple VNC entries in one string
String xml4 = "<graphics type='vnc' port='5900' passwd='a'/>\n" +
"<graphics type='vnc' port='5901' passwd='b'/>";
String expected4 = "<graphics type='vnc' port='5900' passwd='*****'/>\n" +
"<graphics type='vnc' port='5901' passwd='*****'/>";
assertEquals(expected4, LibvirtMigrateCommandWrapper.maskSensitiveInfoInXML(xml4));
}
@Test
public void updateGpuDevicesIfNeededTestNoGpuDevice() throws Exception {
Mockito.doReturn(virtualMachineTOMock).when(migrateCommandMock).getVirtualMachine();

View File

@ -24,7 +24,7 @@ sonar.projectVersion=1.0
sonar.sources=src
sonar.binaries=target/classes
# Exclussions
# Exclusions
sonar.exclusions=**/*Test.java
# Language

View File

@ -1383,8 +1383,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
}
totalAdditionalVms += additional;
long effectiveCpu = (long) so.getCpu() * so.getSpeed();
totalAdditionalCpuUnits += effectiveCpu * additional;
totalAdditionalCpuUnits += so.getCpu() * additional;
totalAdditionalRamMb += so.getRamSize() * additional;
try {

View File

@ -37,6 +37,9 @@ import org.apache.cloudstack.api.response.GetUploadParamsResponse;
import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import com.cloud.api.query.dao.TemplateJoinDao;
@ -57,6 +60,7 @@ import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VMTemplateZoneDao;
import com.cloud.template.TemplateApiService;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentContext;
@ -84,12 +88,14 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
@Inject
private DataCenterDao dataCenterDao;
@Inject
private ImageStoreDao imageStoreDao;
@Inject
private TemplateApiService templateService;
public static final String MINIMUN_AUTOSCALER_SUPPORTED_VERSION = "1.15.0";
protected void updateTemplateDetailsInKubernetesSupportedVersionResponse(
final KubernetesSupportedVersion kubernetesSupportedVersion, KubernetesSupportedVersionResponse response) {
final KubernetesSupportedVersion kubernetesSupportedVersion, KubernetesSupportedVersionResponse response, boolean isRootAdmin) {
TemplateJoinVO template = templateJoinDao.findById(kubernetesSupportedVersion.getIsoId());
if (template == null) {
return;
@ -99,11 +105,14 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
if (template.getState() != null) {
response.setIsoState(template.getState().toString());
}
if (isRootAdmin) {
response.setIsoUrl(template.getUrl());
}
response.setIsoArch(template.getArch().getType());
response.setDirectDownload(template.isDirectDownload());
}
private KubernetesSupportedVersionResponse createKubernetesSupportedVersionResponse(final KubernetesSupportedVersion kubernetesSupportedVersion) {
private KubernetesSupportedVersionResponse createKubernetesSupportedVersionResponse(final KubernetesSupportedVersion kubernetesSupportedVersion, boolean isRootAdmin) {
KubernetesSupportedVersionResponse response = new KubernetesSupportedVersionResponse();
response.setObjectName("kubernetessupportedversion");
response.setId(kubernetesSupportedVersion.getUuid());
@ -122,7 +131,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
response.setSupportsHA(compareSemanticVersions(kubernetesSupportedVersion.getSemanticVersion(),
KubernetesClusterService.MIN_KUBERNETES_VERSION_HA_SUPPORT)>=0);
response.setSupportsAutoscaling(versionSupportsAutoscaling(kubernetesSupportedVersion));
updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion, response);
updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion, response, isRootAdmin);
response.setCreated(kubernetesSupportedVersion.getCreated());
return response;
}
@ -130,8 +139,11 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
private ListResponse<KubernetesSupportedVersionResponse> createKubernetesSupportedVersionListResponse(
List<KubernetesSupportedVersionVO> versions, Integer count) {
List<KubernetesSupportedVersionResponse> responseList = new ArrayList<>();
Account caller = CallContext.current().getCallingAccount();
boolean isRootAdmin = accountManager.isRootAdmin(caller.getId());
for (KubernetesSupportedVersionVO version : versions) {
responseList.add(createKubernetesSupportedVersionResponse(version));
responseList.add(createKubernetesSupportedVersionResponse(version, isRootAdmin));
}
ListResponse<KubernetesSupportedVersionResponse> response = new ListResponse<>();
response.setResponses(responseList, count);
@ -347,6 +359,32 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
return createKubernetesSupportedVersionListResponse(versions, versionsAndCount.second());
}
private void validateImageStoreForZone(Long zoneId, boolean directDownload) {
if (directDownload) {
return;
}
if (zoneId != null) {
List<ImageStoreVO> imageStores = imageStoreDao.listStoresByZoneId(zoneId);
if (CollectionUtils.isEmpty(imageStores)) {
DataCenterVO zone = dataCenterDao.findById(zoneId);
String zoneName = zone != null ? zone.getName() : String.valueOf(zoneId);
throw new InvalidParameterValueException(String.format("Unable to register Kubernetes version ISO. No image store available in zone: %s", zoneName));
}
} else {
List<DataCenterVO> zones = dataCenterDao.listAllZones();
List<String> zonesWithoutStorage = new ArrayList<>();
for (DataCenterVO zone : zones) {
List<ImageStoreVO> imageStores = imageStoreDao.listStoresByZoneId(zone.getId());
if (CollectionUtils.isEmpty(imageStores)) {
zonesWithoutStorage.add(zone.getName());
}
}
if (!zonesWithoutStorage.isEmpty()) {
throw new InvalidParameterValueException(String.format("Unable to register Kubernetes version ISO for all zones. The following zones have no image store: %s", String.join(", ", zonesWithoutStorage)));
}
}
}
private void validateKubernetesSupportedVersion(Long zoneId, String semanticVersion, Integer minimumCpu,
Integer minimumRamSize, boolean isDirectDownload) {
if (minimumCpu == null || minimumCpu < KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_CPU) {
@ -398,6 +436,8 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
}
}
validateImageStoreForZone(zoneId, isDirectDownload);
VMTemplateVO template = null;
try {
VirtualMachineTemplate vmTemplate = registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum, isDirectDownload, arch);
@ -411,7 +451,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
supportedVersionVO = kubernetesSupportedVersionDao.persist(supportedVersionVO);
CallContext.current().putContextParameter(KubernetesSupportedVersion.class, supportedVersionVO.getUuid());
return createKubernetesSupportedVersionResponse(supportedVersionVO);
return createKubernetesSupportedVersionResponse(supportedVersionVO, true);
}
@Override
@ -496,7 +536,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
}
version = kubernetesSupportedVersionDao.findById(versionId);
}
return createKubernetesSupportedVersionResponse(version);
return createKubernetesSupportedVersionResponse(version, true);
}
@Override

View File

@ -50,6 +50,10 @@ public class KubernetesSupportedVersionResponse extends BaseResponse {
@Param(description = "The name of the binaries ISO for Kubernetes supported version")
private String isoName;
@SerializedName(ApiConstants.ISO_URL)
@Param(description = "the URL of the binaries ISO for Kubernetes supported version")
private String isoUrl;
@SerializedName(ApiConstants.ISO_STATE)
@Param(description = "The state of the binaries ISO for Kubernetes supported version")
private String isoState;
@ -134,6 +138,14 @@ public class KubernetesSupportedVersionResponse extends BaseResponse {
this.isoName = isoName;
}
public String getIsoUrl() {
return isoUrl;
}
public void setIsoUrl(String isoUrl) {
this.isoUrl = isoUrl;
}
public String getIsoState() {
return isoState;
}

View File

@ -16,10 +16,15 @@
// under the License.
package com.cloud.kubernetes.version;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -32,6 +37,9 @@ import org.springframework.test.util.ReflectionTestUtils;
import com.cloud.api.query.dao.TemplateJoinDao;
import com.cloud.api.query.vo.TemplateJoinVO;
import com.cloud.cpu.CPU;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.exception.InvalidParameterValueException;
@RunWith(MockitoJUnitRunner.class)
public class KubernetesVersionManagerImplTest {
@ -39,6 +47,12 @@ public class KubernetesVersionManagerImplTest {
@Mock
TemplateJoinDao templateJoinDao;
@Mock
ImageStoreDao imageStoreDao;
@Mock
DataCenterDao dataCenterDao;
@InjectMocks
KubernetesVersionManagerImpl kubernetesVersionManager = new KubernetesVersionManagerImpl();
@ -48,7 +62,7 @@ public class KubernetesVersionManagerImplTest {
Mockito.when(kubernetesSupportedVersion.getIsoId()).thenReturn(1L);
KubernetesSupportedVersionResponse response = new KubernetesSupportedVersionResponse();
kubernetesVersionManager.updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion,
response);
response, true);
Assert.assertNull(ReflectionTestUtils.getField(response, "isoId"));
}
@ -63,13 +77,71 @@ public class KubernetesVersionManagerImplTest {
Mockito.when(templateJoinVO.getUuid()).thenReturn(uuid);
Mockito.when(templateJoinDao.findById(1L)).thenReturn(templateJoinVO);
kubernetesVersionManager.updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion,
response);
response, true);
Assert.assertEquals(uuid, ReflectionTestUtils.getField(response, "isoId"));
Assert.assertNull(ReflectionTestUtils.getField(response, "isoState"));
ObjectInDataStoreStateMachine.State state = ObjectInDataStoreStateMachine.State.Ready;
Mockito.when(templateJoinVO.getState()).thenReturn(state);
kubernetesVersionManager.updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion,
response);
response, true);
Assert.assertEquals(state.toString(), ReflectionTestUtils.getField(response, "isoState"));
}
@Test
public void testValidateImageStoreForZoneWithDirectDownload() {
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", 1L, true);
}
@Test
public void testValidateImageStoreForZoneWithValidZone() {
Long zoneId = 1L;
List<ImageStoreVO> imageStores = Collections.singletonList(Mockito.mock(ImageStoreVO.class));
Mockito.when(imageStoreDao.listStoresByZoneId(zoneId)).thenReturn(imageStores);
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", zoneId, false);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateImageStoreForZoneWithNoImageStore() {
Long zoneId = 1L;
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
Mockito.when(zone.getName()).thenReturn("test-zone");
Mockito.when(dataCenterDao.findById(zoneId)).thenReturn(zone);
Mockito.when(imageStoreDao.listStoresByZoneId(zoneId)).thenReturn(Collections.emptyList());
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", zoneId, false);
}
@Test
public void testValidateImageStoreForAllZonesWithAllValid() {
DataCenterVO zone1 = Mockito.mock(DataCenterVO.class);
Mockito.when(zone1.getId()).thenReturn(1L);
DataCenterVO zone2 = Mockito.mock(DataCenterVO.class);
Mockito.when(zone2.getId()).thenReturn(2L);
List<DataCenterVO> zones = Arrays.asList(zone1, zone2);
Mockito.when(dataCenterDao.listAllZones()).thenReturn(zones);
List<ImageStoreVO> imageStores = Collections.singletonList(Mockito.mock(ImageStoreVO.class));
Mockito.when(imageStoreDao.listStoresByZoneId(1L)).thenReturn(imageStores);
Mockito.when(imageStoreDao.listStoresByZoneId(2L)).thenReturn(imageStores);
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", (Long) null, false);
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateImageStoreForAllZonesWithSomeMissingStorage() {
DataCenterVO zone1 = Mockito.mock(DataCenterVO.class);
Mockito.when(zone1.getId()).thenReturn(1L);
DataCenterVO zone2 = Mockito.mock(DataCenterVO.class);
Mockito.when(zone2.getId()).thenReturn(2L);
Mockito.when(zone2.getName()).thenReturn("zone-without-storage");
List<DataCenterVO> zones = Arrays.asList(zone1, zone2);
Mockito.when(dataCenterDao.listAllZones()).thenReturn(zones);
List<ImageStoreVO> imageStores = Collections.singletonList(Mockito.mock(ImageStoreVO.class));
Mockito.when(imageStoreDao.listStoresByZoneId(1L)).thenReturn(imageStores);
Mockito.when(imageStoreDao.listStoresByZoneId(2L)).thenReturn(Collections.emptyList());
ReflectionTestUtils.invokeMethod(kubernetesVersionManager, "validateImageStoreForZone", (Long) null, false);
}
}

View File

@ -17,6 +17,9 @@
package com.cloud.kubernetes.version;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
@ -25,6 +28,11 @@ import java.util.List;
import java.util.UUID;
import com.cloud.cpu.CPU;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserVO;
import org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd;
import org.apache.cloudstack.api.command.admin.kubernetes.version.DeleteKubernetesSupportedVersionCmd;
import org.apache.cloudstack.api.command.admin.kubernetes.version.UpdateKubernetesSupportedVersionCmd;
@ -63,11 +71,6 @@ import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.template.TemplateApiService;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserVO;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.db.Filter;
@ -75,6 +78,9 @@ import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
@RunWith(MockitoJUnitRunner.class)
public class KubernetesVersionServiceTest {
@ -94,7 +100,11 @@ public class KubernetesVersionServiceTest {
@Mock
private DataCenterDao dataCenterDao;
@Mock
private ImageStoreDao imageStoreDao;
@Mock
private TemplateApiService templateService;
@Mock
private Account accountMock;
AutoCloseable closeable;
@ -123,7 +133,12 @@ public class KubernetesVersionServiceTest {
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone);
List<ImageStoreVO> imageStores = new ArrayList<>();
imageStores.add(Mockito.mock(ImageStoreVO.class));
when(imageStoreDao.listStoresByZoneId(Mockito.anyLong())).thenReturn(imageStores);
TemplateJoinVO templateJoinVO = Mockito.mock(TemplateJoinVO.class);
when(templateJoinVO.getUrl()).thenReturn("https://download.cloudstack.com");
when(templateJoinVO.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready);
when(templateJoinVO.getArch()).thenReturn(CPU.CPUArch.getDefault());
when(templateJoinDao.findById(Mockito.anyLong())).thenReturn(templateJoinVO);
@ -140,19 +155,66 @@ public class KubernetesVersionServiceTest {
@Test
public void listKubernetesSupportedVersionsTest() {
ListKubernetesSupportedVersionsCmd cmd = Mockito.mock(ListKubernetesSupportedVersionsCmd.class);
List<KubernetesSupportedVersionVO> versionVOs = new ArrayList<>();
KubernetesSupportedVersionVO versionVO = Mockito.mock(KubernetesSupportedVersionVO.class);
when(versionVO.getSemanticVersion()).thenReturn(KubernetesVersionService.MIN_KUBERNETES_VERSION);
versionVOs.add(versionVO);
when(kubernetesSupportedVersionDao.findById(Mockito.anyLong())).thenReturn(versionVO);
when(kubernetesSupportedVersionDao.searchAndCount(Mockito.any(SearchCriteria.class),
Mockito.any(Filter.class))).thenReturn(new Pair<>(versionVOs, versionVOs.size()));
ListResponse<KubernetesSupportedVersionResponse> versionsResponse =
kubernetesVersionService.listKubernetesSupportedVersions(cmd);
Assert.assertEquals(versionVOs.size(), versionsResponse.getCount().intValue());
Assert.assertTrue(CollectionUtils.isNotEmpty(versionsResponse.getResponses()));
Assert.assertEquals(versionVOs.size(), versionsResponse.getResponses().size());
CallContext callContextMock = Mockito.mock(CallContext.class);
try (MockedStatic<CallContext> callContextMockedStatic = Mockito.mockStatic(CallContext.class)) {
callContextMockedStatic.when(CallContext::current).thenReturn(callContextMock);
final SearchCriteria<KubernetesSupportedVersionVO> versionSearchCriteria = Mockito.mock(SearchCriteria.class);
when(callContextMock.getCallingAccount()).thenReturn(accountMock);
ListKubernetesSupportedVersionsCmd cmd = Mockito.mock(ListKubernetesSupportedVersionsCmd.class);
List<KubernetesSupportedVersionVO> versionVOs = new ArrayList<>();
KubernetesSupportedVersionVO versionVO = Mockito.mock(KubernetesSupportedVersionVO.class);
when(versionVO.getSemanticVersion()).thenReturn(KubernetesVersionService.MIN_KUBERNETES_VERSION);
versionVOs.add(versionVO);
when(kubernetesSupportedVersionDao.findById(Mockito.anyLong())).thenReturn(versionVO);
when(kubernetesSupportedVersionDao.searchAndCount(Mockito.any(), Mockito.any(Filter.class)))
.thenReturn(new Pair<>(versionVOs, versionVOs.size()));
ListResponse<KubernetesSupportedVersionResponse> versionsResponse =
kubernetesVersionService.listKubernetesSupportedVersions(cmd);
Assert.assertEquals(versionVOs.size(), versionsResponse.getCount().intValue());
Assert.assertTrue(CollectionUtils.isNotEmpty(versionsResponse.getResponses()));
Assert.assertEquals(versionVOs.size(), versionsResponse.getResponses().size());
}
}
@Test
public void listKubernetesSupportedVersionsTestWhenAdmin() {
CallContext callContextMock = Mockito.mock(CallContext.class);
try (MockedStatic<CallContext> callContextMockedStatic = Mockito.mockStatic(CallContext.class)) {
callContextMockedStatic.when(CallContext::current).thenReturn(callContextMock);
ListKubernetesSupportedVersionsCmd cmd = Mockito.mock(ListKubernetesSupportedVersionsCmd.class);
List<KubernetesSupportedVersionVO> versionVOs = new ArrayList<>();
KubernetesSupportedVersionVO versionVO = Mockito.mock(KubernetesSupportedVersionVO.class);
when(versionVO.getSemanticVersion()).thenReturn(KubernetesVersionService.MIN_KUBERNETES_VERSION);
versionVOs.add(versionVO);
when(callContextMock.getCallingAccount()).thenReturn(accountMock);
when(kubernetesSupportedVersionDao.findById(Mockito.anyLong())).thenReturn(versionVO);
when(kubernetesSupportedVersionDao.searchAndCount(Mockito.any(), Mockito.any(Filter.class)))
.thenReturn(new Pair<>(versionVOs, versionVOs.size()));
when(accountManager.isRootAdmin(anyLong())).thenReturn(true);
ListResponse<KubernetesSupportedVersionResponse> response = kubernetesVersionService.listKubernetesSupportedVersions(cmd);
assertNotNull(response.getResponses().get(0).getIsoUrl());
}
}
@Test
public void listKubernetesSupportedVersionsTestWhenOtherUser() {
CallContext callContextMock = Mockito.mock(CallContext.class);
try (MockedStatic<CallContext> callContextMockedStatic = Mockito.mockStatic(CallContext.class)) {
callContextMockedStatic.when(CallContext::current).thenReturn(callContextMock);
ListKubernetesSupportedVersionsCmd cmd = Mockito.mock(ListKubernetesSupportedVersionsCmd.class);
List<KubernetesSupportedVersionVO> versionVOs = new ArrayList<>();
KubernetesSupportedVersionVO versionVO = Mockito.mock(KubernetesSupportedVersionVO.class);
when(versionVO.getSemanticVersion()).thenReturn(KubernetesVersionService.MIN_KUBERNETES_VERSION);
versionVOs.add(versionVO);
when(callContextMock.getCallingAccount()).thenReturn(accountMock);
when(kubernetesSupportedVersionDao.findById(Mockito.anyLong())).thenReturn(versionVO);
when(kubernetesSupportedVersionDao.searchAndCount(Mockito.any(), Mockito.any(Filter.class)))
.thenReturn(new Pair<>(versionVOs, versionVOs.size()));
when(accountManager.isRootAdmin(anyLong())).thenReturn(false);
when(accountMock.getId()).thenReturn(2L);
ListResponse<KubernetesSupportedVersionResponse> response = kubernetesVersionService.listKubernetesSupportedVersions(cmd);
assertNull(response.getResponses().get(0).getIsoUrl());
}
}
@Test(expected = InvalidParameterValueException.class)
@ -224,7 +286,6 @@ public class KubernetesVersionServiceTest {
mockedComponentContext.when(() -> ComponentContext.inject(Mockito.any(RegisterIsoCmd.class))).thenReturn(
new RegisterIsoCmd());
mockedCallContext.when(CallContext::current).thenReturn(callContext);
when(templateService.registerIso(Mockito.any(RegisterIsoCmd.class))).thenReturn(
Mockito.mock(VirtualMachineTemplate.class));
VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class);

View File

@ -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;

View File

@ -350,7 +350,7 @@ public class CephObjectStoreDriverImpl extends BaseObjectStoreDriverImpl {
new AWSStaticCredentialsProvider(
new BasicAWSCredentials(accessKey, secretKey)))
.withEndpointConfiguration(
new AwsClientBuilder.EndpointConfiguration(url, null))
new AwsClientBuilder.EndpointConfiguration(url, "us-east-1"))
.build();
if (client == null) {

View File

@ -63,6 +63,8 @@ import com.cloud.api.storage.LinstorBackupSnapshotCommand;
import com.cloud.api.storage.LinstorRevertBackupSnapshotCommand;
import com.cloud.configuration.Config;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.resource.ResourceState;
import com.cloud.storage.DataStoreRole;
@ -922,9 +924,10 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
_backupsnapshotwait,
VirtualMachineManager.ExecuteInSequence.value());
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(linstorApi, rscName);
final StoragePool pool = (StoragePool) volumeInfo.getDataStore();
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(linstorApi, pool, rscName);
if (optEP.isEmpty()) {
optEP = getLinstorEP(linstorApi, rscName);
optEP = getLinstorEP(linstorApi, pool, rscName);
}
if (optEP.isPresent()) {
@ -1064,13 +1067,29 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
Answer answer = copyVolume(srcData, dstData);
res = new CopyCommandResult(null, answer);
} else {
Answer answer = new Answer(null, false, "noimpl");
res = new CopyCommandResult(null, answer);
res.setResult("Not implemented yet");
throw new CloudRuntimeException("Not implemented for Linstor primary storage.");
}
callback.complete(res);
}
private Host getEnabledClusterHost(StoragePool storagePool, List<String> linstorNodeNames) {
List<HostVO> csHosts;
if (storagePool.getClusterId() != null) {
csHosts = _hostDao.findByClusterId(storagePool.getClusterId());
} else {
csHosts = _hostDao.findByDataCenterId(storagePool.getDataCenterId());
}
Collections.shuffle(csHosts); // so we do not always pick the same host for operations
for (HostVO host : csHosts) {
if (host.getResourceState() == ResourceState.Enabled &&
host.getStatus() == Status.Up &&
linstorNodeNames.contains(host.getName())) {
return host;
}
}
return null;
}
/**
* Tries to get a Linstor cloudstack end point, that is at least diskless.
*
@ -1079,47 +1098,37 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
* @return Optional RemoteHostEndPoint if one could get found.
* @throws ApiException
*/
private Optional<RemoteHostEndPoint> getLinstorEP(DevelopersApi api, String rscName) throws ApiException {
private Optional<RemoteHostEndPoint> getLinstorEP(DevelopersApi api, StoragePool storagePool, String rscName)
throws ApiException {
List<String> linstorNodeNames = LinstorUtil.getLinstorNodeNames(api);
Collections.shuffle(linstorNodeNames); // do not always pick the first linstor node
Host host = null;
for (String nodeName : linstorNodeNames) {
host = _hostDao.findByName(nodeName);
if (host != null && host.getResourceState() == ResourceState.Enabled) {
logger.info(String.format("Linstor: Make resource %s available on node %s ...", rscName, nodeName));
ApiCallRcList answers = api.resourceMakeAvailableOnNode(rscName, nodeName, new ResourceMakeAvailable());
if (!answers.hasError()) {
break; // found working host
} else {
logger.error(
String.format("Linstor: Unable to make resource %s on node %s available: %s",
rscName,
nodeName,
LinstorUtil.getBestErrorMessage(answers)));
}
Host host = getEnabledClusterHost(storagePool, linstorNodeNames);
if (host != null) {
logger.info("Linstor: Make resource {} available on node {} ...", rscName, host.getName());
ApiCallRcList answers = api.resourceMakeAvailableOnNode(
rscName, host.getName(), new ResourceMakeAvailable());
if (answers.hasError()) {
logger.error("Linstor: Unable to make resource {} on node {} available: {}",
rscName, host.getName(), LinstorUtil.getBestErrorMessage(answers));
return Optional.empty();
} else {
return Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host));
}
}
if (host == null)
{
logger.error("Linstor: Couldn't create a resource on any cloudstack host.");
return Optional.empty();
}
else
{
return Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host));
}
logger.error("Linstor: Couldn't create a resource on any cloudstack host.");
return Optional.empty();
}
private Optional<RemoteHostEndPoint> getDiskfullEP(DevelopersApi api, String rscName) throws ApiException {
private Optional<RemoteHostEndPoint> getDiskfullEP(DevelopersApi api, StoragePool storagePool, String rscName)
throws ApiException {
List<com.linbit.linstor.api.model.StoragePool> linSPs = LinstorUtil.getDiskfulStoragePools(api, rscName);
if (linSPs != null) {
for (com.linbit.linstor.api.model.StoragePool sp : linSPs) {
Host host = _hostDao.findByName(sp.getNodeName());
if (host != null && host.getResourceState() == ResourceState.Enabled) {
return Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host));
}
List<String> linstorNodeNames = linSPs.stream()
.map(com.linbit.linstor.api.model.StoragePool::getNodeName)
.collect(Collectors.toList());
Host host = getEnabledClusterHost(storagePool, linstorNodeNames);
if (host != null) {
return Optional.of(RemoteHostEndPoint.getHypervisorHostEndPoint(host));
}
}
logger.error("Linstor: No diskfull host found.");
@ -1200,12 +1209,12 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
VirtualMachineManager.ExecuteInSequence.value());
try {
Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, rscName);
Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, pool, rscName);
if (optEP.isPresent()) {
answer = optEP.get().sendMessage(cmd);
} else {
answer = new Answer(cmd, false, "Unable to get matching Linstor endpoint.");
deleteResourceDefinition(pool, rscName);
throw new CloudRuntimeException("Unable to get matching Linstor endpoint.");
}
} catch (ApiException exc) {
logger.error("copy template failed: ", exc);
@ -1242,12 +1251,12 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
Answer answer;
try {
Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, rscName);
Optional<RemoteHostEndPoint> optEP = getLinstorEP(api, pool, rscName);
if (optEP.isPresent()) {
answer = optEP.get().sendMessage(cmd);
}
else {
answer = new Answer(cmd, false, "Unable to get matching Linstor endpoint.");
throw new CloudRuntimeException("Unable to get matching Linstor endpoint.");
}
} catch (ApiException exc) {
logger.error("copy volume failed: ", exc);
@ -1280,14 +1289,14 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
try {
String devName = restoreResourceFromSnapshot(api, pool, rscName, snapshotName, restoreName);
Optional<RemoteHostEndPoint> optEPAny = getLinstorEP(api, restoreName);
Optional<RemoteHostEndPoint> optEPAny = getLinstorEP(api, pool, restoreName);
if (optEPAny.isPresent()) {
// patch the src device path to the temporary linstor resource
snapshotObject.setPath(devName);
origCmd.setSrcTO(snapshotObject.getTO());
answer = optEPAny.get().sendMessage(origCmd);
} else{
answer = new Answer(origCmd, false, "Unable to get matching Linstor endpoint.");
} else {
throw new CloudRuntimeException("Unable to get matching Linstor endpoint.");
}
} finally {
// delete the temporary resource, noop if already gone
@ -1349,7 +1358,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
VirtualMachineManager.ExecuteInSequence.value());
cmd.setOptions(options);
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(api, rscName);
Optional<RemoteHostEndPoint> optEP = getDiskfullEP(api, pool, rscName);
Answer answer;
if (optEP.isPresent()) {
answer = optEP.get().sendMessage(cmd);

View File

@ -105,7 +105,7 @@ public class StorPoolHelper {
if (snapshotDetails != null) {
return StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDetails.getValue(), true);
} else {
List<SnapshotDataStoreVO> snapshots = snapshotStoreDao.findBySnapshotId(snapshotId);
List<SnapshotDataStoreVO> snapshots = snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
if (!CollectionUtils.isEmpty(snapshots)) {
for (SnapshotDataStoreVO snapshotDataStoreVO : snapshots) {
String name = StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDataStoreVO.getInstallPath(), true);

View File

@ -300,7 +300,7 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
}
protected boolean areLastSnapshotRef(long snapshotId) {
List<SnapshotDataStoreVO> snapshotStoreRefs = _snapshotStoreDao.findBySnapshotId(snapshotId);
List<SnapshotDataStoreVO> snapshotStoreRefs = _snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
if (CollectionUtils.isEmpty(snapshotStoreRefs) || snapshotStoreRefs.size() == 1) {
return true;
}
@ -343,7 +343,7 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
}
if (Snapshot.State.Error.equals(snapshotVO.getState())) {
List<SnapshotDataStoreVO> storeRefs = _snapshotStoreDao.findBySnapshotId(snapshotId);
List<SnapshotDataStoreVO> storeRefs = _snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
List<Long> deletedRefs = new ArrayList<>();
for (SnapshotDataStoreVO ref : storeRefs) {
boolean refZoneIdMatch = false;

View File

@ -29,7 +29,7 @@
</parent>
<properties>
<ads.version>2.0.0.AM25</ads.version>
<ads.version>2.0.0.AM27</ads.version>
<ldap-maven.version>1.3.2</ldap-maven.version>
<ldapunit.version>1.1.3</ldapunit.version>
<groovy.version>1.1-groovy-2.4</groovy.version>

16
pom.xml
View File

@ -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>
@ -87,7 +89,7 @@
<cs.log4j.extras.version>1.2.17</cs.log4j.extras.version>
<!-- Apache Commons versions -->
<cs.codec.version>1.15</cs.codec.version>
<cs.codec.version>1.20.0</cs.codec.version>
<cs.commons-cli.version>1.5.0</cs.commons-cli.version>
<cs.commons-collections.version>4.4</cs.commons-collections.version>
<cs.commons-compress.version>1.26.0</cs.commons-compress.version>
@ -188,6 +190,7 @@
<org.springframework.version>5.3.26</org.springframework.version>
<cs.ini.version>0.5.4</cs.ini.version>
<cs.caffeine.version>3.1.7</cs.caffeine.version>
<cs.protobuf.version>3.25.5</cs.protobuf.version>
</properties>
<distributionManagement>
@ -730,6 +733,17 @@
<artifactId>xml-apis</artifactId>
<version>2.0.2</version>
</dependency>
<!-- enforced protobuf version here as mysql-connector-java is pulling older version (3.19.3) -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${cs.protobuf.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${cs.protobuf.version}</version>
</dependency>
<dependency>
<groupId>com.linbit.linstor.api</groupId>
<artifactId>java-linstor</artifactId>

View File

@ -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

View File

@ -112,7 +112,8 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
, AlertType.ALERT_TYPE_OOBM_AUTH_ERROR
, AlertType.ALERT_TYPE_HA_ACTION
, AlertType.ALERT_TYPE_CA_CERT
, AlertType.ALERT_TYPE_EXTENSION_PATH_NOT_READY);
, AlertType.ALERT_TYPE_EXTENSION_PATH_NOT_READY
, AlertType.ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS);
private static final long INITIAL_CAPACITY_CHECK_DELAY = 30L * 1000L; // Thirty seconds expressed in milliseconds.
@ -313,13 +314,8 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
Math.min(CapacityManager.CapacityCalculateWorkers.value(), hostIds.size())));
for (Long hostId : hostIds) {
futures.put(hostId, executorService.submit(() -> {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
final HostVO host = hostDao.findById(hostId);
_capacityMgr.updateCapacityForHost(host);
}
});
final HostVO host = hostDao.findById(hostId);
_capacityMgr.updateCapacityForHost(host);
return null;
}));
}

View File

@ -50,6 +50,7 @@ import com.cloud.dc.dao.ASNumberRangeDao;
import com.cloud.dc.dao.VlanDetailsDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.network.vpc.VpcGateway;
import com.cloud.network.vpn.Site2SiteVpnManager;
import com.cloud.storage.BucketVO;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
@ -528,6 +529,8 @@ public class ApiResponseHelper implements ResponseGenerator {
@Inject
RoutedIpv4Manager routedIpv4Manager;
@Inject
Site2SiteVpnManager site2SiteVpnManager;
@Inject
ResourceIconManager resourceIconManager;
public static String getPrettyDomainPath(String path) {
@ -3884,6 +3887,16 @@ public class ApiResponseHelper implements ResponseGenerator {
response.setRemoved(result.getRemoved());
response.setIkeVersion(result.getIkeVersion());
response.setSplitConnections(result.getSplitConnections());
Set<String> obsoleteParameters = site2SiteVpnManager.getObsoleteVpnGatewayParameters(result);
if (CollectionUtils.isNotEmpty(obsoleteParameters)) {
response.setContainsObsoleteParameters(obsoleteParameters.toString());
}
Set<String> excludedParameters = site2SiteVpnManager.getExcludedVpnGatewayParameters(result);
if (CollectionUtils.isNotEmpty(excludedParameters)) {
response.setContainsExcludedParameters(excludedParameters.toString());
}
response.setObjectName("vpncustomergateway");
response.setHasAnnotation(annotationDao.hasAnnotations(result.getUuid(), AnnotationService.EntityType.VPN_CUSTOMER_GATEWAY.name(),
_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())));

View File

@ -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,

View File

@ -2409,7 +2409,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(ip.getVlanTag()));
nic.setFormat(AddressFormat.Ip4);
nic.setReservationId(String.valueOf(ip.getVlanTag()));
if(nic.getMacAddress() == null) {
if (nic.getMacAddress() == null) {
nic.setMacAddress(ip.getMacAddress());
}
}
@ -2460,7 +2460,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
nic.setBroadcastUri(network.getBroadcastUri());
nic.setFormat(AddressFormat.Ip4);
if(nic.getMacAddress() == null) {
if (nic.getMacAddress() == null || !_networkModel.isMACUnique(nic.getMacAddress(), network.getId())) {
nic.setMacAddress(_networkModel.getNextAvailableMacAddressInNetwork(network.getId()));
}
}

View File

@ -241,7 +241,7 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
if (nic.getBroadCastUri() == null) {
nic.setBroadcastUri(network.getBroadcastUri());
}
if (nic.getMacAddress() == null) {
if (nic.getMacAddress() == null || !_networkModel.isMACUnique(nic.getMacAddress(), network.getId())) {
nic.setMacAddress(_networkModel.getNextAvailableMacAddressInNetwork(network.getId()));
}
}

View File

@ -594,22 +594,34 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
@Override
public String getNextAvailableMacAddressInNetwork(long networkId) throws InsufficientAddressCapacityException {
NetworkVO network = _networksDao.findById(networkId);
Integer zoneIdentifier = MACIdentifier.value();
if (zoneIdentifier.intValue() == 0) {
zoneIdentifier = Long.valueOf(network.getDataCenterId()).intValue();
if (network == null) {
throw new CloudRuntimeException("Could not find network with id " + networkId);
}
Integer zoneMacIdentifier = Long.valueOf(getMacIdentifier(network.getDataCenterId())).intValue();
String mac;
do {
mac = _networksDao.getNextAvailableMacAddress(networkId, zoneIdentifier);
mac = _networksDao.getNextAvailableMacAddress(networkId, zoneMacIdentifier);
if (mac == null) {
throw new InsufficientAddressCapacityException("Unable to create another mac address", Network.class, networkId);
}
} while(! isMACUnique(mac));
} while (!isMACUnique(mac, networkId));
return mac;
}
private boolean isMACUnique(String mac) {
return (_nicDao.findByMacAddress(mac) == null);
@Override
public String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException {
String macAddressStr = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(macAddress, getMacIdentifier(datacenterId)));
if (!isMACUnique(macAddressStr, networkId)) {
macAddressStr = getNextAvailableMacAddressInNetwork(networkId);
}
return macAddressStr;
}
@Override
public boolean isMACUnique(String mac, long networkId) {
return (_nicDao.findByMacAddress(mac, networkId) == null);
}
@Override
@ -2818,4 +2830,18 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
}
return false;
}
@Override
public long getMacIdentifier(Long dataCenterId) {
long macAddress = 0;
if (dataCenterId == null) {
macAddress = NetworkModel.MACIdentifier.value();
} else {
macAddress = NetworkModel.MACIdentifier.valueIn(dataCenterId);
if (macAddress == 0) {
macAddress = dataCenterId;
}
}
return macAddress;
}
}

View File

@ -293,7 +293,7 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
allocateDirectIp(nic, network, vm, dc, nic.getRequestedIPv4(), nic.getRequestedIPv6());
nic.setReservationStrategy(ReservationStrategy.Create);
if (nic.getMacAddress() == null) {
if (nic.getMacAddress() == null || !_networkModel.isMACUnique(nic.getMacAddress(), network.getId())) {
nic.setMacAddress(_networkModel.getNextAvailableMacAddressInNetwork(network.getId()));
if (nic.getMacAddress() == null) {
throw new InsufficientAddressCapacityException("Unable to allocate more mac addresses", Network.class, network.getId());

View File

@ -53,6 +53,8 @@ public class PodBasedNetworkGuru extends AdapterBase implements NetworkGuru {
DataCenterDao _dcDao;
@Inject
StorageNetworkManager _sNwMgr;
@Inject
NetworkModel _networkModel;
Random _rand = new Random(System.currentTimeMillis());
@ -131,7 +133,8 @@ public class PodBasedNetworkGuru extends AdapterBase implements NetworkGuru {
Integer vlan = result.getVlan();
nic.setIPv4Address(result.getIpAddress());
nic.setMacAddress(NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(result.getMacAddress(), NetworkModel.MACIdentifier.value())));
String macAddress = _networkModel.getUniqueMacAddress(result.getMacAddress(), config.getId(), dest.getDataCenter().getId());
nic.setMacAddress(macAddress);
nic.setIPv4Gateway(pod.getGateway());
nic.setFormat(AddressFormat.Ip4);
String netmask = NetUtils.getCidrNetmask(pod.getCidrSize());

View File

@ -189,8 +189,12 @@ public class PrivateNetworkGuru extends AdapterBase implements NetworkGuru {
PrivateIpVO ipVO = _privateIpDao.allocateIpAddress(network.getDataCenterId(), network.getId(), null);
String vlanTag = BroadcastDomainType.getValue(network.getBroadcastUri());
String netmask = NetUtils.getCidrNetmask(network.getCidr());
String macAddress = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress(), networkModel.getMacIdentifier(network.getDataCenterId())));
if (!networkModel.isMACUnique(macAddress, network.getId())) {
macAddress = networkModel.getNextAvailableMacAddressInNetwork(network.getId());
}
PrivateIpAddress ip =
new PrivateIpAddress(ipVO, vlanTag, network.getGateway(), netmask, NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress(), NetworkModel.MACIdentifier.value())));
new PrivateIpAddress(ipVO, vlanTag, network.getGateway(), netmask, macAddress);
nic.setIPv4Address(ip.getIpAddress());
nic.setIPv4Gateway(ip.getGateway());

View File

@ -37,7 +37,6 @@ import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.offering.NetworkOffering;
import com.cloud.user.Account;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.Nic.ReservationStrategy;
import com.cloud.vm.NicProfile;
import com.cloud.vm.ReservationContext;
@ -47,6 +46,8 @@ public class StorageNetworkGuru extends PodBasedNetworkGuru implements NetworkGu
@Inject
StorageNetworkManager _sNwMgr;
@Inject
NetworkModel _networkModel;
@Inject
NetworkDao _nwDao;
protected StorageNetworkGuru() {
@ -130,7 +131,8 @@ public class StorageNetworkGuru extends PodBasedNetworkGuru implements NetworkGu
vlan = ip.getVlan();
nic.setIPv4Address(ip.getIpAddress());
nic.setMacAddress(NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ip.getMac(), NetworkModel.MACIdentifier.value())));
String macAddress = _networkModel.getUniqueMacAddress(ip.getMac(), network.getId(), dest.getDataCenter().getId());
nic.setMacAddress(macAddress);
nic.setFormat(AddressFormat.Ip4);
nic.setIPv4Netmask(ip.getNetmask());
nic.setBroadcastType(BroadcastDomainType.Storage);

View File

@ -16,6 +16,7 @@
// under the License.
package com.cloud.network.router;
import com.cloud.exception.InsufficientAddressCapacityException;
import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
import com.cloud.network.Network;
@ -24,7 +25,7 @@ import com.cloud.vm.NicProfile;
public interface NicProfileHelper {
public abstract NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGateway, final VirtualRouter router);
public abstract NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGateway, final VirtualRouter router) throws InsufficientAddressCapacityException;
public abstract NicProfile createGuestNicProfileForVpcRouter(final RouterDeploymentDefinition vpcRouterDeploymentDefinition,
Network guestNetwork);

View File

@ -21,6 +21,7 @@ import java.net.URI;
import javax.inject.Inject;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.vm.NicVO;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
@ -61,7 +62,7 @@ public class NicProfileHelperImpl implements NicProfileHelper {
@Override
@DB
public NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGateway, final VirtualRouter router) {
public NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGateway, final VirtualRouter router) throws InsufficientAddressCapacityException {
final Network privateNetwork = _networkModel.getNetwork(privateGateway.getNetworkId());
PrivateIpVO ipVO = _privateIpDao.allocateIpAddress(privateNetwork.getDataCenterId(), privateNetwork.getId(), privateGateway.getIp4Address());
@ -90,14 +91,14 @@ public class NicProfileHelperImpl implements NicProfileHelper {
privateNicProfile.setDeviceId(null);
if (router.getIsRedundantRouter()) {
String newMacAddress = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress(), NetworkModel.MACIdentifier.value()));
privateNicProfile.setMacAddress(newMacAddress);
String newMacAddress = _networkModel.getUniqueMacAddress(ipVO.getMacAddress(), privateNetwork.getId(), privateNetwork.getDataCenterId());
privateNicProfile.setMacAddress(newMacAddress);
}
} else {
final String netmask = NetUtils.getCidrNetmask(privateNetwork.getCidr());
String newMacAddress = _networkModel.getUniqueMacAddress(ipVO.getMacAddress(), privateNetwork.getId(), privateNetwork.getDataCenterId());
final PrivateIpAddress ip =
new PrivateIpAddress(ipVO, privateNetwork.getBroadcastUri().toString(), privateNetwork.getGateway(), netmask,
NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress(), NetworkModel.MACIdentifier.value())));
new PrivateIpAddress(ipVO, privateNetwork.getBroadcastUri().toString(), privateNetwork.getGateway(), netmask, newMacAddress);
final URI netUri = BroadcastDomainType.fromString(ip.getBroadcastUri());
privateNicProfile.setIPv4Address(ip.getIpAddress());

View File

@ -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;
}

View File

@ -17,11 +17,17 @@
package com.cloud.network.vpn;
import java.util.List;
import java.util.Set;
import com.cloud.network.Site2SiteCustomerGateway;
import com.cloud.network.dao.Site2SiteVpnConnectionVO;
import com.cloud.vm.DomainRouterVO;
public interface Site2SiteVpnManager extends Site2SiteVpnService {
Set<String> getExcludedVpnGatewayParameters(Site2SiteCustomerGateway customerGw);
Set<String> getObsoleteVpnGatewayParameters(Site2SiteCustomerGateway customerGw);
boolean cleanupVpnConnectionByVpc(long vpcId);
boolean cleanupVpnGatewayByVpc(long vpcId);

View File

@ -17,15 +17,22 @@
package com.cloud.network.vpn;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.command.user.vpn.CreateVpnConnectionCmd;
@ -41,9 +48,16 @@ import org.apache.cloudstack.api.command.user.vpn.ResetVpnConnectionCmd;
import org.apache.cloudstack.api.command.user.vpn.UpdateVpnCustomerGatewayCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.ConfigKeyScheduledExecutionWrapper;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.alert.AlertManager;
import com.cloud.configuration.Config;
import com.cloud.event.ActionEvent;
import com.cloud.event.ActionEventUtils;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
@ -72,9 +86,11 @@ import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
import com.cloud.projects.Project.ListProjectResourcesCriteria;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.User;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.StringUtils;
import com.cloud.utils.Ternary;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
@ -88,7 +104,52 @@ import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.dao.DomainRouterDao;
@Component
public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpnManager {
public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpnManager, Configurable {
// Configuration keys for VPN gateway cryptographic parameter controls
public static final ConfigKey<String> VpnCustomerGatewayExcludedEncryptionAlgorithms = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.excluded.encryption.algorithms", "",
"Comma-separated list of encryption algorithms that are excluded and cannot be selected by end users for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are aes128, aes192 and aes256 and 3des.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayExcludedHashingAlgorithms = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.excluded.hashing.algorithms", "",
"Comma-separated list of hashing algorithms that are excluded and cannot be selected by end users for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are sha1, sha256, sha384 and sha512 and md5.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayExcludedIkeVersions = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.excluded.ike.versions", "",
"Comma-separated list of IKE versions that are excluded and cannot be selected by end users for VPN Customer Gateways. Allowed values are ikev, ikev1 and ikev2.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayExcludedDhGroup = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.excluded.dh.group", "",
"Comma-separated list of Diffie-Hellman groups that are excluded and cannot be selected by end users for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are modp1024, modp1536, modp2048, modp3072, modp4096, modp6144 and modp8192.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayObsoleteEncryptionAlgorithms = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.obsolete.encryption.algorithms", "",
"Comma-separated list of encryption algorithms that are marked as obsolete/insecure for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are aes128, aes192 and aes256 and 3des.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayObsoleteHashingAlgorithms = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.obsolete.hashing.algorithms", "",
"Comma-separated list of hashing algorithms that are marked as obsolete/insecure for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are sha1, sha256, sha384 and sha512 and md5.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayObsoleteIkeVersions = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.obsolete.ike.versions", "",
"Comma-separated list of IKE versions that are marked as obsolete/insecure for VPN Customer Gateways. Allowed values are ikev, ikev1 and ikev2.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<String> VpnCustomerGatewayObsoleteDhGroup = new ConfigKey<String>(
ConfigKey.CATEGORY_NETWORK, String.class, "vpn.customer.gateway.obsolete.dh.group", "",
"Comma-separated list of Diffie-Hellman groups that are marked as obsolete/insecure for VPN Customer Gateways." +
"Applies to both IKE and ESP phases. Allowed values are modp1024, modp1536, modp2048, modp3072, modp4096, modp6144 and modp8192.",
true, ConfigKey.Scope.Domain);
public static final ConfigKey<Long> VpnCustomerGatewayObsoleteCheckInterval = new ConfigKey<Long>(
ConfigKey.CATEGORY_NETWORK, Long.class, "vpn.customer.gateway.obsolete.check.interval", "0",
"Interval in hours to periodically check VPN customer gateways for obsolete/excluded parameters and generate events and alerts. " +
"Set to 0 to disable. Default: 0 (disabled).",
true, ConfigKey.Scope.Global);
List<Site2SiteVpnServiceProvider> _s2sProviders;
@Inject
@ -117,9 +178,12 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
private IpAddressManager ipAddressManager;
@Inject
private VpcManager vpcManager;
@Inject
private AlertManager _alertMgr;
int _connLimit;
int _subnetsLimit;
private ScheduledExecutorService _vpnCheckExecutor;
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
@ -127,6 +191,7 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
_connLimit = NumbersUtil.parseInt(configs.get(Config.Site2SiteVpnConnectionPerVpnGatewayLimit.key()), 4);
_subnetsLimit = NumbersUtil.parseInt(configs.get(Config.Site2SiteVpnSubnetsPerCustomerGatewayLimit.key()), 10);
assert (_s2sProviders.iterator().hasNext()) : "Did not get injected with a list of S2S providers!";
_vpnCheckExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpnCustomerGateway-ExcludedAndObsoleteCheck"));
return true;
}
@ -146,7 +211,7 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
}
Site2SiteVpnGatewayVO gws = _vpnGatewayDao.findByVpcId(vpcId);
if (gws != null) {
throw new InvalidParameterValueException(String.format("The VPN gateway of VPC %s already existed!", vpc));
throw new InvalidParameterValueException(String.format("The VPN gateway of VPC %s already exists!", vpc));
}
IPAddressVO requestedIp = _ipAddressDao.findById(cmd.getIpAddressId());
@ -187,6 +252,113 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
}
}
private void validateVpnCryptographicParameters(String ikePolicy, String espPolicy, String ikeVersion, Long domainId) {
String excludedEncryption = VpnCustomerGatewayExcludedEncryptionAlgorithms.valueIn(domainId);
String excludedHashing = VpnCustomerGatewayExcludedHashingAlgorithms.valueIn(domainId);
String excludedIkeVersions = VpnCustomerGatewayExcludedIkeVersions.valueIn(domainId);
String excludedDhGroup = VpnCustomerGatewayExcludedDhGroup.valueIn(domainId);
Set<String> excludedParameters = getVpnGatewayParametersInBlockedList(ikePolicy, espPolicy, ikeVersion,
excludedEncryption, excludedHashing, excludedIkeVersions, excludedDhGroup);
if (!excludedParameters.isEmpty()) {
throw new InvalidParameterValueException("The following excluded cryptographic parameter(s) cannot be used in a VPN Customer Gateway: " + excludedParameters.toString());
}
}
@Override
public Set<String> getExcludedVpnGatewayParameters(Site2SiteCustomerGateway customerGw) {
Long domainId = customerGw.getDomainId();
String excludedEncryption = VpnCustomerGatewayExcludedEncryptionAlgorithms.valueIn(domainId);
String excludedHashing = VpnCustomerGatewayExcludedHashingAlgorithms.valueIn(domainId);
String excludedIkeVersions = VpnCustomerGatewayExcludedIkeVersions.valueIn(domainId);
String excludedDhGroup = VpnCustomerGatewayExcludedDhGroup.valueIn(domainId);
return getVpnGatewayParametersInBlockedList(customerGw.getIkePolicy(), customerGw.getEspPolicy(), customerGw.getIkeVersion(),
excludedEncryption, excludedHashing, excludedIkeVersions, excludedDhGroup);
}
@Override
public Set<String> getObsoleteVpnGatewayParameters(Site2SiteCustomerGateway customerGw) {
Long domainId = customerGw.getDomainId();
String obsoleteEncryption = VpnCustomerGatewayObsoleteEncryptionAlgorithms.valueIn(domainId);
String obsoleteHashing = VpnCustomerGatewayObsoleteHashingAlgorithms.valueIn(domainId);
String obsoleteIkeVersions = VpnCustomerGatewayObsoleteIkeVersions.valueIn(domainId);
String obsoleteDhGroup = VpnCustomerGatewayObsoleteDhGroup.valueIn(domainId);
return getVpnGatewayParametersInBlockedList(customerGw.getIkePolicy(), customerGw.getEspPolicy(), customerGw.getIkeVersion(),
obsoleteEncryption, obsoleteHashing, obsoleteIkeVersions, obsoleteDhGroup);
}
private Set<String> getVpnGatewayParametersInBlockedList(String ikePolicy, String espPolicy, String ikeVersion,
String blockedEncryptionList, String blockedHashingList,
String blockedIkeVersionList, String blockedDhGroupList) {
Set<String> blockedParameters = new HashSet<>();
if (StringUtils.isEmpty(blockedEncryptionList)
&& StringUtils.isEmpty(blockedHashingList)
&& StringUtils.isEmpty(blockedIkeVersionList)
&& StringUtils.isEmpty(blockedDhGroupList)) {
return blockedParameters;
}
if (isParameterInList(ikeVersion, blockedIkeVersionList)) {
blockedParameters.add(ikeVersion);
}
Set<String> ikePolicyResult = getVpnGatewayPolicyParametersInBlockedList(ikePolicy, "IKE", blockedEncryptionList, blockedHashingList, blockedDhGroupList);
if (CollectionUtils.isNotEmpty(ikePolicyResult)) {
blockedParameters.addAll(ikePolicyResult);
}
Set<String> espPolicyResult = getVpnGatewayPolicyParametersInBlockedList(espPolicy, "ESP", blockedEncryptionList, blockedHashingList, blockedDhGroupList);
if (CollectionUtils.isNotEmpty(espPolicyResult)) {
blockedParameters.addAll(espPolicyResult);
}
return blockedParameters;
}
private Set<String> getVpnGatewayPolicyParametersInBlockedList(String policy, String policyType, String blockedEncryptionList, String blockedHashingList, String blockedDhGroupList) {
String trimmedPolicy = policy.trim();
String cipherHash = trimmedPolicy.split(";")[0];
String[] parts = cipherHash.split("-");
String encryption = parts[0].trim();
String hashing = parts.length > 1 ? parts[1].trim() : "";
Set<String> blockedParameters = new HashSet<>();
if (isParameterInList(encryption, blockedEncryptionList)) {
blockedParameters.add(encryption);
}
if (isParameterInList(hashing, blockedHashingList)) {
blockedParameters.add(hashing);
}
if (!trimmedPolicy.equals(cipherHash)) {
String dhGroup = trimmedPolicy.split(";")[1].trim();
if (isParameterInList(dhGroup, blockedDhGroupList)) {
blockedParameters.add(dhGroup);
}
}
return blockedParameters;
}
private boolean isParameterInList(String parameter, String list) {
if (StringUtils.isEmpty(list) || StringUtils.isEmpty(parameter)) {
return false;
}
String[] entries = list.split(",");
for (String item : entries) {
if (item != null && item.trim().equalsIgnoreCase(parameter.trim())) {
return true;
}
}
return false;
}
protected void checkCustomerGatewayCidrList(String guestCidrList) {
String[] cidrList = guestCidrList.split(",");
if (cidrList.length > _subnetsLimit) {
@ -235,6 +407,13 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
if (!NetUtils.isValidS2SVpnPolicy("esp", espPolicy)) {
throw new InvalidParameterValueException("The customer gateway ESP policy " + espPolicy + " is invalid!");
}
String ikeVersion = cmd.getIkeVersion();
if (ikeVersion == null) {
ikeVersion = "ike";
}
validateVpnCryptographicParameters(ikePolicy, espPolicy, ikeVersion, owner.getDomainId());
Long ikeLifetime = cmd.getIkeLifetime();
if (ikeLifetime == null) {
// Default value of lifetime is 1 day
@ -264,7 +443,7 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
long accountId = owner.getAccountId();
if (_customerGatewayDao.findByNameAndAccountId(name, accountId) != null) {
throw new InvalidParameterValueException("The customer gateway with name " + name + " already existed!");
throw new InvalidParameterValueException("The customer gateway with name " + name + " already exists!");
}
Boolean splitConnections = cmd.getSplitConnections();
@ -272,11 +451,6 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
splitConnections = false;
}
String ikeVersion = cmd.getIkeVersion();
if (ikeVersion == null) {
ikeVersion = "ike";
}
checkCustomerGatewayCidrList(peerCidrList);
Site2SiteCustomerGatewayVO gw =
@ -374,7 +548,7 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
private void validateVpnConnectionDoesntExist(Site2SiteCustomerGateway customerGateway, Site2SiteVpnGateway vpnGateway) {
if (_vpnConnectionDao.findByVpnGatewayIdAndCustomerGatewayId(vpnGateway.getId(), customerGateway.getId()) != null) {
throw new InvalidParameterValueException(String.format("The vpn connection with customer gateway %s and vpn gateway %s already existed!", customerGateway, vpnGateway));
throw new InvalidParameterValueException(String.format("The vpn connection with customer gateway %s and vpn gateway %s already exists!", customerGateway, vpnGateway));
}
}
@ -521,6 +695,10 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
if (!NetUtils.isValidS2SVpnPolicy("esp", espPolicy)) {
throw new InvalidParameterValueException("The customer gateway ESP policy" + espPolicy + " is invalid!");
}
String ikeVersion = cmd.getIkeVersion();
validateVpnCryptographicParameters(ikePolicy, espPolicy, ikeVersion, gw.getDomainId());
Long ikeLifetime = cmd.getIkeLifetime();
if (ikeLifetime == null) {
// Default value of lifetime is 1 day
@ -550,14 +728,12 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
Boolean splitConnections = cmd.getSplitConnections();
String ikeVersion = cmd.getIkeVersion();
checkCustomerGatewayCidrList(guestCidrList);
long accountId = gw.getAccountId();
Site2SiteCustomerGatewayVO existedGw = _customerGatewayDao.findByNameAndAccountId(name, accountId);
if (existedGw != null && existedGw.getId() != gw.getId()) {
throw new InvalidParameterValueException("The customer gateway with name " + name + " already existed!");
throw new InvalidParameterValueException("The customer gateway with name " + name + " already exists!");
}
gw.setName(name);
@ -977,4 +1153,83 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
return _vpnGatewayDao.findById(id);
}
@Override
public boolean start() {
ConfigKeyScheduledExecutionWrapper runner = new ConfigKeyScheduledExecutionWrapper(
_vpnCheckExecutor,
new CheckVpnCustomerGatewayObsoleteParametersTask(),
VpnCustomerGatewayObsoleteCheckInterval,
3600,
TimeUnit.HOURS);
runner.start();
return true;
}
@Override
public boolean stop() {
if (_vpnCheckExecutor != null) {
_vpnCheckExecutor.shutdownNow();
}
return true;
}
@Override
public String getConfigComponentName() {
return Site2SiteVpnManager.class.getSimpleName();
}
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] { VpnCustomerGatewayExcludedEncryptionAlgorithms, VpnCustomerGatewayExcludedHashingAlgorithms,
VpnCustomerGatewayExcludedIkeVersions, VpnCustomerGatewayExcludedDhGroup, VpnCustomerGatewayObsoleteEncryptionAlgorithms,
VpnCustomerGatewayObsoleteHashingAlgorithms, VpnCustomerGatewayObsoleteIkeVersions, VpnCustomerGatewayObsoleteDhGroup,
VpnCustomerGatewayObsoleteCheckInterval};
}
protected class CheckVpnCustomerGatewayObsoleteParametersTask extends ManagedContextRunnable {
@Override
protected void runInContext() {
List<Site2SiteCustomerGatewayVO> allGateways = _customerGatewayDao.listAll();
int obsoleteCount = 0;
int excludedCount = 0;
for (Site2SiteCustomerGatewayVO gateway : allGateways) {
Set<String> excludedParameters = getExcludedVpnGatewayParameters(gateway);
Set<String> obsoleteParameters = getObsoleteVpnGatewayParameters(gateway);
List<String> message = new ArrayList<>();
if (CollectionUtils.isNotEmpty(excludedParameters)) {
excludedCount++;
message.add("excluded parameter(s) " + excludedParameters.toString());
}
if (CollectionUtils.isNotEmpty(obsoleteParameters)) {
obsoleteCount++;
message.add("obsolete parameter(s) " + obsoleteParameters.toString());
}
if (CollectionUtils.isNotEmpty(message)) {
Account account = _accountDao.findById(gateway.getAccountId());
String description = String.format("VPN customer gateway '%s' (Account: %s) contains %s.",
gateway.getName(), account.getAccountName(), String.join(" and ", message));
ActionEventUtils.onActionEvent(User.UID_SYSTEM, gateway.getAccountId(), gateway.getDomainId(),
EventTypes.EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS, description,
gateway.getId(), Site2SiteCustomerGateway.class.getSimpleName());
}
}
List<String> message = new ArrayList<>();
if (excludedCount > 0) {
message.add("excluded parameters: " + excludedCount);
}
if (obsoleteCount > 0) {
message.add("obsolete parameters: " + obsoleteCount);
}
if (CollectionUtils.isNotEmpty(message)) {
String subject = String.format("VPN customer gateways using " + String.join(", ", message));
_alertMgr.sendAlert(AlertService.AlertType.ALERT_TYPE_VPN_GATEWAY_OBSOLETE_PARAMETERS, 0L, 0L, subject, null);
}
}
}
}

View File

@ -720,6 +720,7 @@ import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.DeploymentPlanningManager;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEvent;
@ -761,6 +762,7 @@ import com.cloud.network.IpAddressManagerImpl;
import com.cloud.network.Network;
import com.cloud.network.NetworkModel;
import com.cloud.network.Networks;
import com.cloud.network.vpn.Site2SiteVpnManagerImpl;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.LoadBalancerDao;
@ -4778,6 +4780,14 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
final Map<String, Object> capabilities = new HashMap<>();
final Account caller = getCaller();
Long domainId = cmd.getDomainId();
if (domainId == null) {
domainId = caller.getDomainId();
} else {
Domain domain = _domainDao.findById(domainId);
_accountService.checkAccess(caller, domain);
}
final boolean isCallerRootAdmin = _accountService.isRootAdmin(caller.getId());
final boolean isCallerAdmin = isCallerRootAdmin || _accountService.isAdmin(caller.getId());
boolean securityGroupsEnabled = false;
@ -4812,7 +4822,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
final boolean allowUserExpungeRecoverVolume = (VolumeApiServiceImpl.AllowUserExpungeRecoverVolume.valueIn(caller.getId()) | isCallerAdmin);
final boolean allowUserForceStopVM = (UserVmManager.AllowUserForceStopVm.valueIn(caller.getId()) | isCallerAdmin);
final boolean allowUserViewAllDomainAccounts = (QueryService.AllowUserViewAllDomainAccounts.valueIn(caller.getDomainId()));
final boolean allowUserViewAllDomainAccounts = (QueryService.AllowUserViewAllDomainAccounts.valueIn(domainId));
final boolean kubernetesServiceEnabled = Boolean.parseBoolean(_configDao.getValue("cloud.kubernetes.service.enabled"));
final boolean kubernetesClusterExperimentalFeaturesEnabled = Boolean.parseBoolean(_configDao.getValue("cloud.kubernetes.cluster.experimental.features.enabled"));
@ -4865,9 +4875,53 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
}
capabilities.put(ApiConstants.ADDITONAL_CONFIG_ENABLED, UserVmManager.EnableAdditionalVmConfig.valueIn(caller.getId()));
Map<String, Object> vpnParams = getVpnCustomerGatewayParameters(domainId);
if (!vpnParams.isEmpty()) {
capabilities.put(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS, vpnParams);
}
return capabilities;
}
private Map<String, Object> getVpnCustomerGatewayParameters(Long domainId) {
Map<String, Object> vpnParams = new HashMap<>();
String excludedEncryption = Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms.valueIn(domainId);
String excludedHashing = Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms.valueIn(domainId);
String excludedIkeVersions = Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions.valueIn(domainId);
String excludedDhGroup = Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup.valueIn(domainId);
String obsoleteEncryption = Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms.valueIn(domainId);
String obsoleteHashing = Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms.valueIn(domainId);
String obsoleteIkeVersions = Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions.valueIn(domainId);
String obsoleteDhGroup = Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup.valueIn(domainId);
if (!excludedEncryption.isEmpty()) {
vpnParams.put("excludedencryptionalgorithms", excludedEncryption);
}
if (!obsoleteEncryption.isEmpty()) {
vpnParams.put("obsoleteencryptionalgorithms", obsoleteEncryption);
}
if (!excludedHashing.isEmpty()) {
vpnParams.put("excludedhashingalgorithms", excludedHashing);
}
if (!obsoleteHashing.isEmpty()) {
vpnParams.put("obsoletehashingalgorithms", obsoleteHashing);
}
if (!excludedIkeVersions.isEmpty()) {
vpnParams.put("excludedikeversions", excludedIkeVersions);
}
if (!obsoleteIkeVersions.isEmpty()) {
vpnParams.put("obsoleteikeversions", obsoleteIkeVersions);
}
if (!excludedDhGroup.isEmpty()) {
vpnParams.put("excludeddhgroups", excludedDhGroup);
}
if (!obsoleteDhGroup.isEmpty()) {
vpnParams.put("obsoletedhgroups", obsoleteDhGroup);
}
return vpnParams;
}
@Override
public GuestOSVO getGuestOs(final Long guestOsId) {
return _guestOSDao.findById(guestOsId);

View File

@ -1801,14 +1801,18 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
protected String getStoragePoolNonDestroyedVolumesLog(long storagePoolId) {
StringBuilder sb = new StringBuilder();
List<VolumeVO> nonDestroyedVols = volumeDao.findByPoolId(storagePoolId, null).stream().filter(vol -> vol.getState() != Volume.State.Destroy).collect(Collectors.toList());
List<VolumeVO> nonDestroyedVols = volumeDao.findByPoolId(storagePoolId, null);
VMInstanceVO volInstance;
List<String> logMessageInfo = new ArrayList<>();
sb.append("[");
for (VolumeVO vol : nonDestroyedVols) {
volInstance = _vmInstanceDao.findById(vol.getInstanceId());
logMessageInfo.add(String.format("Volume [%s] (attached to VM [%s])", vol.getUuid(), volInstance.getUuid()));
if (volInstance != null) {
logMessageInfo.add(String.format("Volume [%s] (attached to VM [%s])", vol.getUuid(), volInstance.getUuid()));
} else {
logMessageInfo.add(String.format("Volume [%s]", vol.getUuid()));
}
}
sb.append(String.join(", ", logMessageInfo));
sb.append("]");
@ -2110,41 +2114,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
}
}
//destroy snapshots in destroying state in snapshot_store_ref
List<SnapshotDataStoreVO> ssSnapshots = _snapshotStoreDao.listByState(ObjectInDataStoreStateMachine.State.Destroying);
for (SnapshotDataStoreVO snapshotDataStoreVO : ssSnapshots) {
String snapshotUuid = null;
SnapshotVO snapshot = null;
final String storeRole = snapshotDataStoreVO.getRole().toString().toLowerCase();
if (logger.isDebugEnabled()) {
snapshot = _snapshotDao.findById(snapshotDataStoreVO.getSnapshotId());
if (snapshot == null) {
logger.warn(String.format("Did not find snapshot [ID: %d] for which store reference is in destroying state; therefore, it cannot be destroyed.", snapshotDataStoreVO.getSnapshotId()));
continue;
}
snapshotUuid = snapshot.getUuid();
}
try {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Verifying if snapshot [%s] is in destroying state in %s data store ID: %d.", snapshotUuid, storeRole, snapshotDataStoreVO.getDataStoreId()));
}
SnapshotInfo snapshotInfo = snapshotFactory.getSnapshot(snapshotDataStoreVO.getSnapshotId(), snapshotDataStoreVO.getDataStoreId(), snapshotDataStoreVO.getRole());
if (snapshotInfo != null) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Snapshot [%s] in destroying state found in %s data store [%s]; therefore, it will be destroyed.", snapshotUuid, storeRole, snapshotInfo.getDataStore().getUuid()));
}
_snapshotService.deleteSnapshot(snapshotInfo);
} else if (logger.isDebugEnabled()) {
logger.debug(String.format("Did not find snapshot [%s] in destroying state in %s data store ID: %d.", snapshotUuid, storeRole, snapshotDataStoreVO.getDataStoreId()));
}
} catch (Exception e) {
logger.error("Failed to delete snapshot [{}] from storage due to: [{}].", snapshot, e.getMessage());
if (logger.isDebugEnabled()) {
logger.debug("Failed to delete snapshot [{}] from storage.", snapshot, e);
}
}
}
cleanupSnapshotsFromStoreRefInDestroyingState();
cleanupSecondaryStorage(recurring);
List<VolumeVO> vols = volumeDao.listVolumesToBeDestroyed(new Date(System.currentTimeMillis() - ((long)StorageCleanupDelay.value() << 10)));
@ -2184,20 +2154,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
}
}
// remove snapshots in Error state
List<SnapshotVO> snapshots = _snapshotDao.listAllByStatus(Snapshot.State.Error);
for (SnapshotVO snapshotVO : snapshots) {
try {
List<SnapshotDataStoreVO> storeRefs = _snapshotStoreDao.findBySnapshotId(snapshotVO.getId());
for (SnapshotDataStoreVO ref : storeRefs) {
_snapshotStoreDao.expunge(ref.getId());
}
_snapshotDao.expunge(snapshotVO.getId());
} catch (Exception e) {
logger.error("Unable to destroy snapshot [{}] due to: [{}].", snapshotVO, e.getMessage());
logger.debug("Unable to destroy snapshot [{}].", snapshotVO, e);
}
}
removeSnapshotsInErrorStatus();
// destroy uploaded volumes in abandoned/error state
List<VolumeDataStoreVO> volumeDataStores = _volumeDataStoreDao.listByVolumeState(Volume.State.UploadError, Volume.State.UploadAbandoned);
@ -2298,6 +2255,56 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
}
}
private void cleanupSnapshotsFromStoreRefInDestroyingState() {
List<SnapshotDataStoreVO> storeRefSnapshotsInDestroyingState = _snapshotStoreDao.listByState(ObjectInDataStoreStateMachine.State.Destroying);
for (SnapshotDataStoreVO snapshotDataStoreVO : storeRefSnapshotsInDestroyingState) {
SnapshotVO snapshot = _snapshotDao.findById(snapshotDataStoreVO.getSnapshotId());
if (snapshot == null) {
logger.warn("Did not find snapshot [ID: {}] for which store reference is in destroying state; therefore, it cannot be destroyed.", snapshotDataStoreVO.getSnapshotId());
continue;
}
deleteSnapshot(snapshot, snapshotDataStoreVO);
}
}
private void deleteSnapshot(SnapshotVO snapshot, SnapshotDataStoreVO snapshotDataStoreVO) {
if (snapshot == null || snapshotDataStoreVO == null) {
return;
}
try {
final String snapshotUuid = snapshot.getUuid();
final String storeRole = snapshotDataStoreVO.getRole().toString().toLowerCase();
logger.debug("Snapshot [{}] is in {} state on {} data store ID: {}.", snapshotUuid, snapshotDataStoreVO.getState(), storeRole, snapshotDataStoreVO.getDataStoreId());
SnapshotInfo snapshotInfo = snapshotFactory.getSnapshotIncludingRemoved(snapshotDataStoreVO.getSnapshotId(), snapshotDataStoreVO.getDataStoreId(), snapshotDataStoreVO.getRole());
if (snapshotInfo != null) {
logger.debug("Snapshot [{}] in {} state found on {} data store [{}], it will be deleted.", snapshotUuid, snapshotDataStoreVO.getState(), storeRole, snapshotInfo.getDataStore().getUuid());
_snapshotService.deleteSnapshot(snapshotInfo);
} else {
logger.debug("Did not find snapshot [{}] in {} state on {} data store ID: {}.", snapshotUuid, snapshotDataStoreVO.getState(), storeRole, snapshotDataStoreVO.getDataStoreId());
}
} catch (Exception e) {
logger.error("Failed to delete snapshot [{}] from storage due to: [{}].", snapshot, e.getMessage(), e);
}
}
private void removeSnapshotsInErrorStatus() {
List<SnapshotVO> snapshotsInErrorStatus = _snapshotDao.listAllByStatusIncludingRemoved(Snapshot.State.Error);
for (SnapshotVO snapshotVO : snapshotsInErrorStatus) {
try {
List<SnapshotDataStoreVO> storeRefSnapshotsInErrorStatus = _snapshotStoreDao.findBySnapshotId(snapshotVO.getId());
for (SnapshotDataStoreVO snapshotDataStoreVO : storeRefSnapshotsInErrorStatus) {
deleteSnapshot(snapshotVO, snapshotDataStoreVO);
_snapshotStoreDao.expunge(snapshotDataStoreVO.getId());
}
_snapshotDao.expunge(snapshotVO.getId());
} catch (Exception e) {
logger.error("Unable to destroy snapshot [{}] due to: [{}].", snapshotVO, e.getMessage());
logger.debug("Unable to destroy snapshot [{}].", snapshotVO, e);
}
}
}
protected boolean isVolumeSuspectedDestroyDuplicateOfVmVolume(VolumeVO gcVolume) {
if (gcVolume.getPath() == null) {
return false;

View File

@ -27,6 +27,7 @@ import com.cloud.exception.StorageConflictException;
import com.cloud.storage.StorageManager;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.Profiler;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager;
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
@ -200,12 +201,13 @@ public class StoragePoolMonitor implements Listener {
}
@Override
public synchronized boolean processDisconnect(long agentId, Status state) {
public boolean processDisconnect(long agentId, Status state) {
return processDisconnect(agentId, null, null, state);
}
@Override
public synchronized boolean processDisconnect(long agentId, String uuid, String name, Status state) {
public boolean processDisconnect(long agentId, String uuid, String name, Status state) {
logger.debug("Starting disconnect for Agent [id: {}, uuid: {}, name: {}]", agentId, uuid, name);
Host host = _storageManager.getHost(agentId);
if (host == null) {
logger.warn("Agent [id: {}, uuid: {}, name: {}] not found, not disconnecting pools", agentId, uuid, name);
@ -213,38 +215,52 @@ public class StoragePoolMonitor implements Listener {
}
if (host.getType() != Host.Type.Routing) {
logger.debug("Host [id: {}, uuid: {}, name: {}] is not of type {}, skip", agentId, uuid, name, Host.Type.Routing);
return false;
}
logger.debug("Looking for connected Storage Pools for Host [id: {}, uuid: {}, name: {}]", agentId, uuid, name);
List<StoragePoolHostVO> storagePoolHosts = _storageManager.findStoragePoolsConnectedToHost(host.getId());
if (storagePoolHosts == null) {
if (logger.isTraceEnabled()) {
logger.trace("No pools to disconnect for host: {}", host);
}
logger.debug("No pools to disconnect for host: {}", host);
return true;
}
logger.debug("Found {} pools to disconnect for host: {}", storagePoolHosts.size(), host);
boolean disconnectResult = true;
for (StoragePoolHostVO storagePoolHost : storagePoolHosts) {
int storagePoolHostsSize = storagePoolHosts.size();
for (int i = 0; i < storagePoolHostsSize; i++) {
StoragePoolHostVO storagePoolHost = storagePoolHosts.get(i);
logger.debug("Processing disconnect from Storage Pool {} ({} of {}) for host: {}", storagePoolHost.getPoolId(), i, storagePoolHostsSize, host);
StoragePoolVO pool = _poolDao.findById(storagePoolHost.getPoolId());
if (pool == null) {
logger.debug("No Storage Pool found with id {} ({} of {}) for host: {}", storagePoolHost.getPoolId(), i, storagePoolHostsSize, host);
continue;
}
if (!pool.isShared()) {
logger.debug("Storage Pool {} ({}) ({} of {}) is not shared for host: {}, ignore disconnect", pool.getName(), pool.getUuid(), i, storagePoolHostsSize, host);
continue;
}
// Handle only PowerFlex pool for now, not to impact other pools behavior
if (pool.getPoolType() != StoragePoolType.PowerFlex) {
logger.debug("Storage Pool {} ({}) ({} of {}) is not of type {} for host: {}, ignore disconnect", pool.getName(), pool.getUuid(), i, storagePoolHostsSize, pool.getPoolType(), host);
continue;
}
logger.debug("Sending disconnect to Storage Pool {} ({}) ({} of {}) for host: {}", pool.getName(), pool.getUuid(), i, storagePoolHostsSize, host);
Profiler disconnectProfiler = new Profiler();
try {
disconnectProfiler.start();
_storageManager.disconnectHostFromSharedPool(host, pool);
} catch (Exception e) {
logger.error("Unable to disconnect host {} from storage pool {} due to {}", host, pool, e.toString());
disconnectResult = false;
} finally {
disconnectProfiler.stop();
long disconnectDuration = disconnectProfiler.getDurationInMillis() / 1000;
logger.debug("Finished disconnect with result {} from Storage Pool {} ({}) ({} of {}) for host: {}, duration: {} secs", disconnectResult, pool.getName(), pool.getUuid(), i, storagePoolHostsSize, host, disconnectDuration);
}
}

View File

@ -843,7 +843,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
protected Pair<List<SnapshotDataStoreVO>, List<Long>> getStoreRefsAndZonesForSnapshotDelete(long snapshotId, Long zoneId) {
List<SnapshotDataStoreVO> snapshotStoreRefs = new ArrayList<>();
List<SnapshotDataStoreVO> allSnapshotStoreRefs = _snapshotStoreDao.findBySnapshotId(snapshotId);
List<SnapshotDataStoreVO> allSnapshotStoreRefs = _snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId);
List<Long> zoneIds = new ArrayList<>();
if (zoneId != null) {
DataCenterVO zone = dataCenterDao.findById(zoneId);
@ -1782,23 +1782,23 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
if (asyncBackup) {
backupSnapshotExecutor.schedule(new BackupSnapshotTask(snapshotOnPrimary, snapshotBackupRetries - 1, snapshotStrategy, zoneIds, poolIds), 0, TimeUnit.SECONDS);
} else {
SnapshotInfo backupedSnapshot = snapshotStrategy.backupSnapshot(snapshotOnPrimary);
if (backupedSnapshot != null) {
SnapshotInfo backedUpSnapshot = snapshotStrategy.backupSnapshot(snapshotOnPrimary);
if (backedUpSnapshot != null) {
snapshotStrategy.postSnapshotCreation(snapshotOnPrimary);
}
}
}
protected class BackupSnapshotTask extends ManagedContextRunnable {
SnapshotInfo snapshot;
SnapshotInfo snapshotOnPrimary;
int attempts;
SnapshotStrategy snapshotStrategy;
List<Long> zoneIds;
List<Long> poolIds;
public BackupSnapshotTask(SnapshotInfo snap, int maxRetries, SnapshotStrategy strategy, List<Long> zoneIds, List<Long> poolIds) {
snapshot = snap;
public BackupSnapshotTask(SnapshotInfo snapshot, int maxRetries, SnapshotStrategy strategy, List<Long> zoneIds, List<Long> poolIds) {
snapshotOnPrimary = snapshot;
attempts = maxRetries;
snapshotStrategy = strategy;
this.zoneIds = zoneIds;
@ -1810,17 +1810,16 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
try {
logger.debug("Value of attempts is " + (snapshotBackupRetries - attempts));
if (Boolean.TRUE.equals(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value()) && CollectionUtils.isEmpty(poolIds)) {
SnapshotInfo backupedSnapshot = snapshotStrategy.backupSnapshot(snapshot);
SnapshotInfo backupedSnapshot = snapshotStrategy.backupSnapshot(snapshotOnPrimary);
if (backupedSnapshot != null) {
snapshotStrategy.postSnapshotCreation(snapshot);
copyNewSnapshotToZones(snapshot.getId(), snapshot.getDataCenterId(), zoneIds);
snapshotStrategy.postSnapshotCreation(snapshotOnPrimary);
copyNewSnapshotToZones(snapshotOnPrimary.getId(), snapshotOnPrimary.getDataCenterId(), zoneIds);
}
}
if (CollectionUtils.isNotEmpty(poolIds)) {
for (Long poolId: poolIds) {
copySnapshotOnPool(snapshot, snapshotStrategy, poolId);
copySnapshotOnPool(snapshotOnPrimary, snapshotStrategy, poolId);
}
}
} catch (final Exception e) {
@ -1830,11 +1829,11 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
private void decriseBackupSnapshotAttempts() {
if (attempts >= 0) {
logger.debug("Backing up of snapshot failed, for snapshot {}, left with {} more attempts", snapshot, attempts);
backupSnapshotExecutor.schedule(new BackupSnapshotTask(snapshot, --attempts, snapshotStrategy, zoneIds, poolIds), snapshotBackupRetryInterval, TimeUnit.SECONDS);
logger.debug("Backing up of snapshot failed, for snapshot {}, left with {} more attempts", snapshotOnPrimary, attempts);
backupSnapshotExecutor.schedule(new BackupSnapshotTask(snapshotOnPrimary, --attempts, snapshotStrategy, zoneIds, poolIds), snapshotBackupRetryInterval, TimeUnit.SECONDS);
} else {
logger.debug("Done with {} attempts in backing up of snapshot {}", snapshotBackupRetries, snapshot.getSnapshotVO());
snapshotSrv.cleanupOnSnapshotBackupFailure(snapshot);
logger.debug("Done with {} attempts in backing up of snapshot {}", snapshotBackupRetries, snapshotOnPrimary.getSnapshotVO());
snapshotSrv.cleanupOnSnapshotBackupFailure(snapshotOnPrimary);
}
}
}
@ -2058,7 +2057,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
public void markVolumeSnapshotsAsDestroyed(Volume volume) {
List<SnapshotVO> snapshots = _snapshotDao.listByVolumeId(volume.getId());
for (SnapshotVO snapshot: snapshots) {
List<SnapshotDataStoreVO> snapshotDataStoreVOs = _snapshotStoreDao.findBySnapshotId(snapshot.getId());
List<SnapshotDataStoreVO> snapshotDataStoreVOs = _snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshot.getId());
if (CollectionUtils.isEmpty(snapshotDataStoreVOs)) {
snapshot.setState(Snapshot.State.Destroyed);
_snapshotDao.update(snapshot.getId(), snapshot);

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -7189,6 +7189,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
throw new CloudRuntimeException("Unable to find suitable destination to migrate VM " + vm.getInstanceName());
}
logger.info("Starting migration of VM {} from host {} to host {} ", vm.getInstanceName(), srcHostId, dest.getHost().getId());
collectVmDiskAndNetworkStatistics(vmId, State.Running);
_itMgr.migrate(vm.getUuid(), srcHostId, dest);
return findMigratedVm(vm.getId(), vm.getType());
@ -7260,6 +7261,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
private DeployDestination checkVmMigrationDestination(VMInstanceVO vm, Host srcHost, Host destinationHost) throws VirtualMachineMigrationException {
if (destinationHost == null) {
logger.error("Destination host is null for migration of VM: {}", vm.getInstanceName());
return null;
}
if (destinationHost.getId() == srcHost.getId()) {

View File

@ -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 "

View File

@ -81,4 +81,6 @@
<bean id="DPDKHelper" class="com.cloud.hypervisor.kvm.dpdk.DpdkHelperImpl" />
<bean id="domainHelper" class="com.cloud.utils.DomainHelper" />
</beans>

View File

@ -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);
}
}

View File

@ -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;

View File

@ -246,8 +246,10 @@ public class Ipv6AddressManagerTest {
Mockito.when(network.getIp6Cidr()).thenReturn("2001:db8:100::/64");
Mockito.when(network.getIp6Gateway()).thenReturn("2001:db8:100::1");
Mockito.when(network.getId()).thenReturn(1L);
Mockito.when(networkModel.getNetworkIp6Dns(network, dc)).thenReturn(new Pair<>("2001:db8::53:1", "2001:db8::53:2"));
Mockito.when(networkModel.isMACUnique("1e:00:b1:00:0a:f6", 1L)).thenReturn(true);
String expected = "2001:db8:100:0:1c00:b1ff:fe00:af6";

View File

@ -129,6 +129,24 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel {
return null;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#getUniqueMacAddress(long, long, long)
*/
@Override
public String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException {
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#isMACUnique(String, long)
*/
@Override
public boolean isMACUnique(String mac, long networkId) {
// TODO Auto-generated method stub
return true;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#getPublicIpAddress(long)
*/

View File

@ -0,0 +1,944 @@
/*
* 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.network.vpn;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Site2SiteVpnConnection;
import com.cloud.network.Site2SiteVpnConnection.State;
import com.cloud.network.Site2SiteVpnGateway;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.Site2SiteCustomerGatewayDao;
import com.cloud.network.dao.Site2SiteCustomerGatewayVO;
import com.cloud.network.dao.Site2SiteVpnConnectionDao;
import com.cloud.network.dao.Site2SiteVpnConnectionVO;
import com.cloud.network.dao.Site2SiteVpnGatewayDao;
import com.cloud.network.dao.Site2SiteVpnGatewayVO;
import com.cloud.network.element.Site2SiteVpnServiceProvider;
import com.cloud.network.vpc.VpcManager;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserVO;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.DomainRouterVO;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.command.user.vpn.CreateVpnConnectionCmd;
import org.apache.cloudstack.api.command.user.vpn.CreateVpnCustomerGatewayCmd;
import org.apache.cloudstack.api.command.user.vpn.CreateVpnGatewayCmd;
import org.apache.cloudstack.api.command.user.vpn.DeleteVpnConnectionCmd;
import org.apache.cloudstack.api.command.user.vpn.DeleteVpnCustomerGatewayCmd;
import org.apache.cloudstack.api.command.user.vpn.DeleteVpnGatewayCmd;
import org.apache.cloudstack.api.command.user.vpn.ResetVpnConnectionCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.Silent.class)
public class Site2SiteVpnManagerImplTest {
@Mock
private Site2SiteCustomerGatewayDao _customerGatewayDao;
@Mock
private Site2SiteVpnGatewayDao _vpnGatewayDao;
@Mock
private Site2SiteVpnConnectionDao _vpnConnectionDao;
@Mock
private VpcDao _vpcDao;
@Mock
private IPAddressDao _ipAddressDao;
@Mock
private VpcManager _vpcMgr;
@Mock
private AccountManager _accountMgr;
@Mock
private AnnotationDao annotationDao;
@Mock
private List<Site2SiteVpnServiceProvider> _s2sProviders;
@Mock
VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
@InjectMocks
private Site2SiteVpnManagerImpl site2SiteVpnManager;
private AccountVO account;
private UserVO user;
private VpcVO vpc;
private IPAddressVO ipAddress;
private Site2SiteVpnGatewayVO vpnGateway;
private Site2SiteCustomerGatewayVO customerGateway;
private Site2SiteVpnConnectionVO vpnConnection;
private static final Long ACCOUNT_ID = 1L;
private static final Long DOMAIN_ID = 2L;
private static final Long VPC_ID = 3L;
private static final Long VPN_GATEWAY_ID = 4L;
private static final Long CUSTOMER_GATEWAY_ID = 5L;
private static final Long VPN_CONNECTION_ID = 6L;
private static final Long IP_ADDRESS_ID = 7L;
@Before
public void setUp() throws Exception {
account = new AccountVO("testaccount", DOMAIN_ID, "networkdomain", Account.Type.NORMAL, UUID.randomUUID().toString());
account.setId(ACCOUNT_ID);
user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone",
UUID.randomUUID().toString(), User.Source.UNKNOWN);
CallContext.register(user, account);
vpc = mock(VpcVO.class);
when(vpc.getId()).thenReturn(VPC_ID);
when(vpc.getAccountId()).thenReturn(ACCOUNT_ID);
when(vpc.getDomainId()).thenReturn(DOMAIN_ID);
when(vpc.getCidr()).thenReturn("10.0.0.0/16");
ipAddress = mock(IPAddressVO.class);
when(ipAddress.getId()).thenReturn(IP_ADDRESS_ID);
when(ipAddress.getVpcId()).thenReturn(VPC_ID);
vpnGateway = mock(Site2SiteVpnGatewayVO.class);
when(vpnGateway.getId()).thenReturn(VPN_GATEWAY_ID);
when(vpnGateway.getVpcId()).thenReturn(VPC_ID);
when(vpnGateway.getAccountId()).thenReturn(ACCOUNT_ID);
when(vpnGateway.getDomainId()).thenReturn(DOMAIN_ID);
customerGateway = mock(Site2SiteCustomerGatewayVO.class);
when(customerGateway.getId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(customerGateway.getAccountId()).thenReturn(ACCOUNT_ID);
when(customerGateway.getDomainId()).thenReturn(DOMAIN_ID);
when(customerGateway.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(customerGateway.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(customerGateway.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(customerGateway.getIkeVersion()).thenReturn("ike");
vpnConnection = new Site2SiteVpnConnectionVO(ACCOUNT_ID, DOMAIN_ID, VPN_GATEWAY_ID, CUSTOMER_GATEWAY_ID, false);
vpnConnection.setState(State.Pending);
when(_accountMgr.getAccount(ACCOUNT_ID)).thenReturn(account);
doNothing().when(_accountMgr).checkAccess(any(Account.class), nullable(SecurityChecker.AccessType.class), anyBoolean(), any());
}
@After
public void tearDown() throws Exception {
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms);
resetConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup);
CallContext.unregister();
}
private void setConfigKeyValue(ConfigKey<String> configKey, String value) {
try {
Field valueField = ConfigKey.class.getDeclaredField("_value");
valueField.setAccessible(true);
valueField.set(configKey, value);
Field dynamicField = ConfigKey.class.getDeclaredField("_isDynamic");
dynamicField.setAccessible(true);
dynamicField.setBoolean(configKey, false);
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException("Failed to set ConfigKey value", e);
}
}
private void resetConfigKeyValue(ConfigKey<String> configKey) {
try {
Field valueField = ConfigKey.class.getDeclaredField("_value");
valueField.setAccessible(true);
valueField.set(configKey, null);
Field dynamicField = ConfigKey.class.getDeclaredField("_isDynamic");
dynamicField.setAccessible(true);
dynamicField.setBoolean(configKey, true);
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException("Failed to reset ConfigKey value", e);
}
}
@Test
public void testCreateVpnGatewaySuccess() {
CreateVpnGatewayCmd cmd = mock(CreateVpnGatewayCmd.class);
when(cmd.getVpcId()).thenReturn(VPC_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(cmd.isDisplay()).thenReturn(true);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(null);
when(_ipAddressDao.listByAssociatedVpc(VPC_ID, true)).thenReturn(List.of(ipAddress));
when(_vpnGatewayDao.persist(any(Site2SiteVpnGatewayVO.class))).thenReturn(vpnGateway);
Site2SiteVpnGateway result = site2SiteVpnManager.createVpnGateway(cmd);
assertNotNull(result);
verify(_vpnGatewayDao).persist(any(Site2SiteVpnGatewayVO.class));
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateVpnGatewayInvalidVpc() {
CreateVpnGatewayCmd cmd = mock(CreateVpnGatewayCmd.class);
when(cmd.getVpcId()).thenReturn(VPC_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(_vpcDao.findById(VPC_ID)).thenReturn(null);
site2SiteVpnManager.createVpnGateway(cmd);
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateVpnGatewayAlreadyExists() {
CreateVpnGatewayCmd cmd = mock(CreateVpnGatewayCmd.class);
when(cmd.getVpcId()).thenReturn(VPC_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(vpnGateway);
site2SiteVpnManager.createVpnGateway(cmd);
}
@Test(expected = CloudRuntimeException.class)
public void testCreateVpnGatewayNoSourceNatIp() {
CreateVpnGatewayCmd cmd = mock(CreateVpnGatewayCmd.class);
when(cmd.getVpcId()).thenReturn(VPC_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(null);
when(_ipAddressDao.listByAssociatedVpc(VPC_ID, true)).thenReturn(new ArrayList<>());
site2SiteVpnManager.createVpnGateway(cmd);
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidIp() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("invalid-ip");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("invalid-ip")).thenReturn(false);
netUtilsMock.when(() -> NetUtils.verifyDomainName("invalid-ip")).thenReturn(false);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidCidrList() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("invalid-cidr");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("invalid-cidr")).thenReturn(false);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidIkePolicy() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("invalid-policy");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "invalid-policy")).thenReturn(false);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidEspPolicy() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("invalid-policy");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "invalid-policy")).thenReturn(false);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayWithExcludedParameters() throws Exception {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getName()).thenReturn("test-gateway");
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIpsecPsk()).thenReturn("test-psk");
when(cmd.getIkePolicy()).thenReturn("3des-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getIkeVersion()).thenReturn("ike");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "3des-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayDuplicateName() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getName()).thenReturn("test-gateway");
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
when(_customerGatewayDao.findByNameAndAccountId("test-gateway", ACCOUNT_ID)).thenReturn(customerGateway);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidIkeLifetime() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getIkeLifetime()).thenReturn(86401L);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayInvalidEspLifetime() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspLifetime()).thenReturn(86401L);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList("192.168.1.0/24")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayTooManySubnets() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
String tooManyCidrs = "192.168.1.0/24,192.168.2.0/24,192.168.3.0/24,192.168.4.0/24,192.168.5.0/24," +
"192.168.6.0/24,192.168.7.0/24,192.168.8.0/24,192.168.9.0/24,192.168.10.0/24,192.168.11.0/24";
when(cmd.getGuestCidrList()).thenReturn(tooManyCidrs);
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList(tooManyCidrs)).thenReturn(true);
netUtilsMock.when(() -> NetUtils.getCleanIp4CidrList(tooManyCidrs)).thenReturn(tooManyCidrs);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateCustomerGatewayOverlappingSubnets() {
CreateVpnCustomerGatewayCmd cmd = mock(CreateVpnCustomerGatewayCmd.class);
when(cmd.getGatewayIp()).thenReturn("1.2.3.4");
when(cmd.getGuestCidrList()).thenReturn("192.168.1.0/24,192.168.1.0/25");
when(cmd.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
String cidrList = "192.168.1.0/24,192.168.1.0/25";
netUtilsMock.when(() -> NetUtils.isValidIp4("1.2.3.4")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidCidrList(cidrList)).thenReturn(true);
netUtilsMock.when(() -> NetUtils.getCleanIp4CidrList(cidrList)).thenReturn(cidrList);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha256;modp2048")).thenReturn(true);
netUtilsMock.when(() -> NetUtils.isNetworksOverlap("192.168.1.0/24", "192.168.1.0/25")).thenReturn(true);
site2SiteVpnManager.createCustomerGateway(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateVpnConnectionCidrOverlapWithVpc() {
CreateVpnConnectionCmd cmd = mock(CreateVpnConnectionCmd.class);
when(cmd.getVpnGatewayId()).thenReturn(VPN_GATEWAY_ID);
when(cmd.getCustomerGatewayId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
Site2SiteCustomerGatewayVO customerGw = mock(Site2SiteCustomerGatewayVO.class);
when(customerGw.getGuestCidrList()).thenReturn("10.0.0.0/24");
when(customerGw.getAccountId()).thenReturn(ACCOUNT_ID);
when(customerGw.getDomainId()).thenReturn(DOMAIN_ID);
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGw);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.findByVpnGatewayIdAndCustomerGatewayId(VPN_GATEWAY_ID, CUSTOMER_GATEWAY_ID)).thenReturn(null);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(vpnGateway);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
try (MockedStatic<NetUtils> netUtilsMock = Mockito.mockStatic(NetUtils.class)) {
netUtilsMock.when(() -> NetUtils.isNetworksOverlap("10.0.0.0/16", "10.0.0.0/24")).thenReturn(true);
site2SiteVpnManager.createVpnConnection(cmd);
}
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateVpnConnectionExceedsLimit() {
CreateVpnConnectionCmd cmd = mock(CreateVpnConnectionCmd.class);
when(cmd.getVpnGatewayId()).thenReturn(VPN_GATEWAY_ID);
when(cmd.getCustomerGatewayId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(cmd.getEntityOwnerId()).thenReturn(ACCOUNT_ID);
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGateway);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.findByVpnGatewayIdAndCustomerGatewayId(VPN_GATEWAY_ID, CUSTOMER_GATEWAY_ID)).thenReturn(null);
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(vpnGateway);
when(_vpcDao.findById(VPC_ID)).thenReturn(vpc);
List<Site2SiteVpnConnectionVO> existingConns = new ArrayList<>();
for (int i = 0; i < 4; i++) {
existingConns.add(mock(Site2SiteVpnConnectionVO.class));
}
when(_vpnConnectionDao.listByVpnGatewayId(VPN_GATEWAY_ID)).thenReturn(existingConns);
site2SiteVpnManager.createVpnConnection(cmd);
}
@Test
public void testDeleteCustomerGatewaySuccess() {
DeleteVpnCustomerGatewayCmd cmd = mock(DeleteVpnCustomerGatewayCmd.class);
when(cmd.getId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGateway);
when(_vpnConnectionDao.listByCustomerGatewayId(CUSTOMER_GATEWAY_ID)).thenReturn(new ArrayList<>());
boolean result = site2SiteVpnManager.deleteCustomerGateway(cmd);
assertTrue(result);
verify(_customerGatewayDao).remove(CUSTOMER_GATEWAY_ID);
}
@Test(expected = InvalidParameterValueException.class)
public void testDeleteCustomerGatewayWithConnections() {
DeleteVpnCustomerGatewayCmd cmd = mock(DeleteVpnCustomerGatewayCmd.class);
when(cmd.getId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGateway);
when(_vpnConnectionDao.listByCustomerGatewayId(CUSTOMER_GATEWAY_ID)).thenReturn(List.of(vpnConnection));
site2SiteVpnManager.deleteCustomerGateway(cmd);
}
@Test
public void testDeleteVpnGatewaySuccess() {
DeleteVpnGatewayCmd cmd = mock(DeleteVpnGatewayCmd.class);
when(cmd.getId()).thenReturn(VPN_GATEWAY_ID);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.listByVpnGatewayId(VPN_GATEWAY_ID)).thenReturn(new ArrayList<>());
boolean result = site2SiteVpnManager.deleteVpnGateway(cmd);
assertTrue(result);
verify(_vpnGatewayDao).remove(VPN_GATEWAY_ID);
}
@Test(expected = InvalidParameterValueException.class)
public void testDeleteVpnGatewayWithConnections() {
DeleteVpnGatewayCmd cmd = mock(DeleteVpnGatewayCmd.class);
when(cmd.getId()).thenReturn(VPN_GATEWAY_ID);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.listByVpnGatewayId(VPN_GATEWAY_ID)).thenReturn(List.of(vpnConnection));
site2SiteVpnManager.deleteVpnGateway(cmd);
}
@Test
public void testDeleteVpnConnectionSuccess() throws ResourceUnavailableException {
DeleteVpnConnectionCmd cmd = mock(DeleteVpnConnectionCmd.class);
when(cmd.getId()).thenReturn(VPN_CONNECTION_ID);
when(_vpnConnectionDao.findById(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
vpnConnection.setState(State.Pending);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpcMgr.applyStaticRouteForVpcVpnIfNeeded(anyLong(), anyBoolean())).thenReturn(true);
boolean result = site2SiteVpnManager.deleteVpnConnection(cmd);
assertTrue(result);
verify(_vpnConnectionDao).remove(VPN_CONNECTION_ID);
}
@Test
public void testStartVpnConnectionSuccess() throws ResourceUnavailableException {
when(_vpnConnectionDao.acquireInLockTable(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
vpnConnection.setState(State.Pending);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
Site2SiteVpnServiceProvider provider = mock(Site2SiteVpnServiceProvider.class);
when(provider.startSite2SiteVpn(any(Site2SiteVpnConnection.class))).thenReturn(true);
when(_s2sProviders.iterator()).thenReturn(List.of(provider).iterator());
when(_vpnConnectionDao.persist(any(Site2SiteVpnConnectionVO.class))).thenReturn(vpnConnection);
when(_vpcMgr.applyStaticRouteForVpcVpnIfNeeded(anyLong(), anyBoolean())).thenReturn(true);
Site2SiteVpnConnection result = site2SiteVpnManager.startVpnConnection(VPN_CONNECTION_ID);
assertNotNull(result);
verify(_vpnConnectionDao, org.mockito.Mockito.atLeastOnce()).persist(any(Site2SiteVpnConnectionVO.class));
}
@Test(expected = InvalidParameterValueException.class)
public void testStartVpnConnectionWrongState() throws ResourceUnavailableException {
when(_vpnConnectionDao.acquireInLockTable(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
vpnConnection.setState(State.Connected);
site2SiteVpnManager.startVpnConnection(VPN_CONNECTION_ID);
}
@Test
public void testResetVpnConnectionSuccess() throws ResourceUnavailableException {
ResetVpnConnectionCmd cmd = mock(ResetVpnConnectionCmd.class);
when(cmd.getId()).thenReturn(VPN_CONNECTION_ID);
when(_vpnConnectionDao.findById(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
vpnConnection.setState(State.Connected);
when(_vpnConnectionDao.acquireInLockTable(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
Site2SiteVpnServiceProvider provider = mock(Site2SiteVpnServiceProvider.class);
when(provider.stopSite2SiteVpn(any(Site2SiteVpnConnection.class))).thenReturn(true);
when(provider.startSite2SiteVpn(any(Site2SiteVpnConnection.class))).thenReturn(true);
when(_s2sProviders.iterator()).thenReturn(List.of(provider).iterator());
when(_vpnConnectionDao.persist(any(Site2SiteVpnConnectionVO.class))).thenReturn(vpnConnection);
when(_vpcMgr.applyStaticRouteForVpcVpnIfNeeded(anyLong(), anyBoolean())).thenReturn(true);
Site2SiteVpnConnection result = site2SiteVpnManager.resetVpnConnection(cmd);
assertNotNull(result);
}
@Test
public void testCleanupVpnConnectionByVpc() {
when(_vpnConnectionDao.listByVpcId(VPC_ID)).thenReturn(List.of(vpnConnection));
boolean result = site2SiteVpnManager.cleanupVpnConnectionByVpc(VPC_ID);
assertTrue(result);
verify(_vpnConnectionDao).remove(vpnConnection.getId());
}
@Test
public void testCleanupVpnGatewayByVpc() {
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(vpnGateway);
when(_vpnConnectionDao.listByVpnGatewayId(VPN_GATEWAY_ID)).thenReturn(new ArrayList<>());
boolean result = site2SiteVpnManager.cleanupVpnGatewayByVpc(VPC_ID);
assertTrue(result);
verify(_vpnGatewayDao).remove(VPN_GATEWAY_ID);
}
@Test
public void testCleanupVpnGatewayByVpcNotFound() {
when(_vpnGatewayDao.findByVpcId(VPC_ID)).thenReturn(null);
boolean result = site2SiteVpnManager.cleanupVpnGatewayByVpc(VPC_ID);
assertTrue(result);
verify(_vpnGatewayDao, never()).remove(anyLong());
}
@Test
public void testGetConnectionsForRouter() {
DomainRouterVO router = mock(DomainRouterVO.class);
when(router.getVpcId()).thenReturn(VPC_ID);
when(_vpnConnectionDao.listByVpcId(VPC_ID)).thenReturn(List.of(vpnConnection));
List<Site2SiteVpnConnectionVO> result = site2SiteVpnManager.getConnectionsForRouter(router);
assertNotNull(result);
assertEquals(1, result.size());
}
@Test
public void testGetConnectionsForRouterNoVpc() {
DomainRouterVO router = mock(DomainRouterVO.class);
when(router.getVpcId()).thenReturn(null);
List<Site2SiteVpnConnectionVO> result = site2SiteVpnManager.getConnectionsForRouter(router);
assertNotNull(result);
assertTrue(result.isEmpty());
}
@Test
public void testDeleteCustomerGatewayByAccount() {
when(_customerGatewayDao.listByAccountId(ACCOUNT_ID)).thenReturn(List.of(customerGateway));
when(_vpnConnectionDao.listByCustomerGatewayId(CUSTOMER_GATEWAY_ID)).thenReturn(new ArrayList<>());
boolean result = site2SiteVpnManager.deleteCustomerGatewayByAccount(ACCOUNT_ID);
assertTrue(result);
verify(_customerGatewayDao).remove(CUSTOMER_GATEWAY_ID);
}
@Test
public void testReconnectDisconnectedVpnByVpc() throws ResourceUnavailableException {
Site2SiteVpnConnectionVO conn = mock(Site2SiteVpnConnectionVO.class);
when(conn.getId()).thenReturn(VPN_CONNECTION_ID);
when(conn.getState()).thenReturn(State.Disconnected);
when(conn.getCustomerGatewayId()).thenReturn(CUSTOMER_GATEWAY_ID);
when(conn.getVpnGatewayId()).thenReturn(VPN_GATEWAY_ID);
when(_vpnConnectionDao.listByVpcId(VPC_ID)).thenReturn(List.of(conn));
when(_customerGatewayDao.findById(CUSTOMER_GATEWAY_ID)).thenReturn(customerGateway);
when(_vpnConnectionDao.acquireInLockTable(VPN_CONNECTION_ID)).thenReturn(conn);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
Site2SiteVpnServiceProvider provider = mock(Site2SiteVpnServiceProvider.class);
when(provider.startSite2SiteVpn(any(Site2SiteVpnConnection.class))).thenReturn(true);
when(_s2sProviders.iterator()).thenReturn(List.of(provider).iterator());
when(_vpnConnectionDao.persist(any(Site2SiteVpnConnectionVO.class))).thenReturn(conn);
when(_vpcMgr.applyStaticRouteForVpcVpnIfNeeded(anyLong(), anyBoolean())).thenReturn(true);
site2SiteVpnManager.reconnectDisconnectedVpnByVpc(VPC_ID);
verify(_vpnConnectionDao, org.mockito.Mockito.atLeastOnce()).persist(any(Site2SiteVpnConnectionVO.class));
}
@Test
public void testUpdateVpnConnection() {
when(_vpnConnectionDao.findById(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
when(_vpnConnectionDao.update(anyLong(), any(Site2SiteVpnConnectionVO.class))).thenReturn(true);
when(_vpnConnectionDao.findById(VPN_CONNECTION_ID)).thenReturn(vpnConnection);
Site2SiteVpnConnection result = site2SiteVpnManager.updateVpnConnection(VPN_CONNECTION_ID, "custom-id", true);
assertNotNull(result);
}
@Test
public void testUpdateVpnGateway() {
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
when(_vpnGatewayDao.update(anyLong(), any(Site2SiteVpnGatewayVO.class))).thenReturn(true);
when(_vpnGatewayDao.findById(VPN_GATEWAY_ID)).thenReturn(vpnGateway);
Site2SiteVpnGateway result = site2SiteVpnManager.updateVpnGateway(VPN_GATEWAY_ID, "custom-id", true);
assertNotNull(result);
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedIkeVersion() throws Exception {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ikev1");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "ikev1");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded IKE version", result.isEmpty());
assertEquals("Should detect excluded IKE version", "[ikev1]", result.toString());
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedEncryption() throws Exception {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("3des-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded encryption algorithm", result.isEmpty());
assertEquals("Should detect excluded encryption algorithm", "[3des]", result.toString());
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedHashing() throws Exception {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-md5;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "aes128");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "md5");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded algorithms", result.isEmpty());
assertEquals("Should detect excluded algorithms", "[aes128, md5]", result.toString());
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedDhGroup() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp1024");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "modp1024");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded DH group", result.isEmpty());
assertEquals("Should detect excluded DH group", "[modp1024]", result.toString());
}
@Test
public void testVpnGatewayContainsExcludedParametersNoExcludedParameters() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertTrue("Should not detect excluded parameters when none are configured", result.isEmpty());
}
@Test
public void testVpnGatewayContainsExcludedParametersWithExcludedEspPolicy() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("3des-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayExcludedDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getExcludedVpnGatewayParameters(gw);
assertFalse("Should detect excluded encryption in ESP policy", result.isEmpty());
assertEquals("Should detect excluded encryption in ESP policy", "[3des]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteIkeVersion() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ikev1");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "ikev1");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete IKE version", result.isEmpty());
assertEquals("Should detect obsolete IKE version", "[ikev1]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteEncryption() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("3des-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete encryption algorithm", result.isEmpty());
assertEquals("Should detect obsolete encryption algorithm", "[3des]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteHashing() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-md5;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "md5");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete hashing algorithm", result.isEmpty());
assertEquals("Should detect obsolete hashing algorithm", "[md5]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteDhGroup() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp1024");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "modp1024");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete DH group", result.isEmpty());
assertEquals("Should detect obsolete DH group", "[modp1024]", result.toString());
}
@Test
public void testVpnGatewayContainsObsoleteParametersNoObsoleteParameters() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertTrue("Should not detect obsolete parameters when none are configured", result.isEmpty());
}
@Test
public void testVpnGatewayContainsObsoleteParametersWithObsoleteEspPolicy() {
Site2SiteCustomerGatewayVO gw = mock(Site2SiteCustomerGatewayVO.class);
when(gw.getIkePolicy()).thenReturn("aes128-sha256;modp2048");
when(gw.getEspPolicy()).thenReturn("3des-sha256;modp2048");
when(gw.getIkeVersion()).thenReturn("ike");
when(gw.getDomainId()).thenReturn(DOMAIN_ID);
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteIkeVersions, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteEncryptionAlgorithms, "3des");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteHashingAlgorithms, "");
setConfigKeyValue(Site2SiteVpnManagerImpl.VpnCustomerGatewayObsoleteDhGroup, "");
java.util.Set<String> result = site2SiteVpnManager.getObsoleteVpnGatewayParameters(gw);
assertFalse("Should detect obsolete encryption in ESP policy", result.isEmpty());
assertEquals("Should detect obsolete encryption in ESP policy", "[3des]", result.toString());
}
}

View File

@ -164,7 +164,7 @@ public class SnapshotManagerImplTest {
Mockito.when(ref1.getDataStoreId()).thenReturn(2L);
Mockito.when(ref1.getRole()).thenReturn(DataStoreRole.Image);
List<SnapshotDataStoreVO> snapshotStoreList = List.of(ref, ref1);
Mockito.when(snapshotStoreDao.findBySnapshotId(snapshotId)).thenReturn(snapshotStoreList);
Mockito.when(snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId)).thenReturn(snapshotStoreList);
Mockito.when(dataStoreManager.getStoreZoneId(1L, DataStoreRole.Image)).thenReturn(100L);
Mockito.when(dataStoreManager.getStoreZoneId(2L, DataStoreRole.Image)).thenReturn(101L);
Pair<List<SnapshotDataStoreVO>, List<Long>> pair = snapshotManager.getStoreRefsAndZonesForSnapshotDelete(snapshotId, null);
@ -189,7 +189,7 @@ public class SnapshotManagerImplTest {
Mockito.when(ref2.getDataStoreId()).thenReturn(3L);
Mockito.when(ref2.getRole()).thenReturn(DataStoreRole.Image);
List<SnapshotDataStoreVO> snapshotStoreList = List.of(ref, ref1, ref2);
Mockito.when(snapshotStoreDao.findBySnapshotId(snapshotId)).thenReturn(snapshotStoreList);
Mockito.when(snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId)).thenReturn(snapshotStoreList);
Mockito.when(dataStoreManager.getStoreZoneId(1L, DataStoreRole.Image)).thenReturn(zoneId);
Mockito.when(dataStoreManager.getStoreZoneId(2L, DataStoreRole.Primary)).thenReturn(zoneId);
Mockito.when(dataStoreManager.getStoreZoneId(3L, DataStoreRole.Image)).thenReturn(2L);

View File

@ -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));
}

View File

@ -141,6 +141,24 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel {
return null;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#getUniqueMacAddress(long, long, long)
*/
@Override
public String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException {
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#isMACUnique(String, long)
*/
@Override
public boolean isMACUnique(String mac, long networkId) {
// TODO Auto-generated method stub
return true;
}
/* (non-Javadoc)
* @see com.cloud.network.NetworkModel#getPublicIpAddress(long)
*/

View File

@ -23,7 +23,6 @@ import com.cloud.network.Site2SiteVpnConnection;
import com.cloud.network.Site2SiteVpnGateway;
import com.cloud.network.dao.Site2SiteVpnConnectionVO;
import com.cloud.network.vpn.Site2SiteVpnManager;
import com.cloud.network.vpn.Site2SiteVpnService;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
import com.cloud.vm.DomainRouterVO;
@ -41,11 +40,13 @@ import org.apache.cloudstack.api.command.user.vpn.UpdateVpnCustomerGatewayCmd;
import org.springframework.stereotype.Component;
import javax.naming.ConfigurationException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Component
public class MockSite2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpnManager, Site2SiteVpnService {
public class MockSite2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpnManager {
/* (non-Javadoc)
* @see com.cloud.network.vpn.Site2SiteVpnService#createVpnGateway(org.apache.cloudstack.api.commands.CreateVpnGatewayCmd)
@ -275,4 +276,16 @@ public class MockSite2SiteVpnManagerImpl extends ManagerBase implements Site2Sit
return null;
}
@Override
public Set<String> getExcludedVpnGatewayParameters(Site2SiteCustomerGateway customerGw) {
// TODO Auto-generated method stub
return new HashSet<>();
}
@Override
public Set<String> getObsoleteVpnGatewayParameters(Site2SiteCustomerGateway customerGw) {
// TODO Auto-generated method stub
return new HashSet<>();
}
}

View File

@ -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;
}
}

Some files were not shown because too many files have changed in this diff Show More