diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index f426cef03cc..6fc673cc2c9 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -2776,219 +2776,218 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra boolean ipv6 = false; try (CheckedReservation networkReservation = new CheckedReservation(owner, domainId, Resource.ResourceType.network, null, null, 1L, reservationDao, _resourceLimitMgr)) { - - if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { - ipv6 = true; - } - // Validate zone - if (zone.getNetworkType() == NetworkType.Basic) { - // In Basic zone the network should have aclType=Domain, domainId=1, subdomainAccess=true - if (aclType == null || aclType != ACLType.Domain) { - throw new InvalidParameterValueException("Only AclType=Domain can be specified for network creation in Basic zone"); + if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { + ipv6 = true; } + // Validate zone + if (zone.getNetworkType() == NetworkType.Basic) { + // In Basic zone the network should have aclType=Domain, domainId=1, subdomainAccess=true + if (aclType == null || aclType != ACLType.Domain) { + throw new InvalidParameterValueException("Only AclType=Domain can be specified for network creation in Basic zone"); + } - // Only one guest network is supported in Basic zone - final List guestNetworks = _networksDao.listByZoneAndTrafficType(zone.getId(), TrafficType.Guest); - if (!guestNetworks.isEmpty()) { - throw new InvalidParameterValueException("Can't have more than one Guest network in zone with network type " + NetworkType.Basic); - } + // Only one guest network is supported in Basic zone + final List guestNetworks = _networksDao.listByZoneAndTrafficType(zone.getId(), TrafficType.Guest); + if (!guestNetworks.isEmpty()) { + throw new InvalidParameterValueException("Can't have more than one Guest network in zone with network type " + NetworkType.Basic); + } - // if zone is basic, only Shared network offerings w/o source nat service are allowed - if (!(ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { - throw new InvalidParameterValueException("For zone of type " + NetworkType.Basic + " only offerings of " + "guestType " + GuestType.Shared + " with disabled " - + Service.SourceNat.getName() + " service are allowed"); - } + // if zone is basic, only Shared network offerings w/o source nat service are allowed + if (!(ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { + throw new InvalidParameterValueException("For zone of type " + NetworkType.Basic + " only offerings of " + "guestType " + GuestType.Shared + " with disabled " + + Service.SourceNat.getName() + " service are allowed"); + } - if (domainId == null || domainId != Domain.ROOT_DOMAIN) { - throw new InvalidParameterValueException("Guest network in Basic zone should be dedicated to ROOT domain"); - } + if (domainId == null || domainId != Domain.ROOT_DOMAIN) { + throw new InvalidParameterValueException("Guest network in Basic zone should be dedicated to ROOT domain"); + } - if (subdomainAccess == null) { - subdomainAccess = true; - } else if (!subdomainAccess) { - throw new InvalidParameterValueException("Subdomain access should be set to true for the" + " guest network in the Basic zone"); - } + if (subdomainAccess == null) { + subdomainAccess = true; + } else if (!subdomainAccess) { + throw new InvalidParameterValueException("Subdomain access should be set to true for the" + " guest network in the Basic zone"); + } - if (vlanId == null) { - vlanId = Vlan.UNTAGGED; - } else { - if (!vlanId.equalsIgnoreCase(Vlan.UNTAGGED)) { - throw new InvalidParameterValueException("Only vlan " + Vlan.UNTAGGED + " can be created in " + "the zone of type " + NetworkType.Basic); + if (vlanId == null) { + vlanId = Vlan.UNTAGGED; + } else { + if (!vlanId.equalsIgnoreCase(Vlan.UNTAGGED)) { + throw new InvalidParameterValueException("Only vlan " + Vlan.UNTAGGED + " can be created in " + "the zone of type " + NetworkType.Basic); + } + } + + } else if (zone.getNetworkType() == NetworkType.Advanced) { + if (zone.isSecurityGroupEnabled()) { + if (isolatedPvlan != null) { + throw new InvalidParameterValueException("Isolated Private VLAN is not supported with security group!"); + } + // Only Account specific Isolated network with sourceNat service disabled are allowed in security group + // enabled zone + if ((ntwkOff.getGuestType() != GuestType.Shared) && (ntwkOff.getGuestType() != GuestType.L2)) { + throw new InvalidParameterValueException("Only shared or L2 guest network can be created in security group enabled zone"); + } + if (_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)) { + throw new InvalidParameterValueException("Service SourceNat is not allowed in security group enabled zone"); + } + } + + //don't allow eip/elb networks in Advance zone + if (ntwkOff.isElasticIp() || ntwkOff.isElasticLb()) { + throw new InvalidParameterValueException("Elastic IP and Elastic LB services are supported in zone of type " + NetworkType.Basic); } } - } else if (zone.getNetworkType() == NetworkType.Advanced) { - if (zone.isSecurityGroupEnabled()) { - if (isolatedPvlan != null) { - throw new InvalidParameterValueException("Isolated Private VLAN is not supported with security group!"); - } - // Only Account specific Isolated network with sourceNat service disabled are allowed in security group - // enabled zone - if ((ntwkOff.getGuestType() != GuestType.Shared) && (ntwkOff.getGuestType() != GuestType.L2)) { - throw new InvalidParameterValueException("Only shared or L2 guest network can be created in security group enabled zone"); - } - if (_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)) { - throw new InvalidParameterValueException("Service SourceNat is not allowed in security group enabled zone"); + if (ipv6 && !GuestType.Shared.equals(ntwkOff.getGuestType())) { + _networkModel.checkIp6CidrSizeEqualTo64(ip6Cidr); + } + + //TODO(VXLAN): Support VNI specified + // VlanId can be specified only when network offering supports it + final boolean vlanSpecified = vlanId != null; + if (vlanSpecified != ntwkOff.isSpecifyVlan()) { + if (vlanSpecified) { + if (!isSharedNetworkWithoutSpecifyVlan(ntwkOff) && !isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { + throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false"); + } + } else { + throw new InvalidParameterValueException("Vlan has to be specified; corresponding offering says specifyVlan=true"); } } - //don't allow eip/elb networks in Advance zone - if (ntwkOff.isElasticIp() || ntwkOff.isElasticLb()) { - throw new InvalidParameterValueException("Elastic IP and Elastic LB services are supported in zone of type " + NetworkType.Basic); - } - } - - if (ipv6 && !GuestType.Shared.equals(ntwkOff.getGuestType())) { - _networkModel.checkIp6CidrSizeEqualTo64(ip6Cidr); - } - - //TODO(VXLAN): Support VNI specified - // VlanId can be specified only when network offering supports it - final boolean vlanSpecified = vlanId != null; - if (vlanSpecified != ntwkOff.isSpecifyVlan()) { if (vlanSpecified) { - if (!isSharedNetworkWithoutSpecifyVlan(ntwkOff) && !isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { - throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false"); + URI uri = encodeVlanIdIntoBroadcastUri(vlanId, pNtwk); + // Aux: generate secondary URI for secondary VLAN ID (if provided) for performing checks + URI secondaryUri = StringUtils.isNotBlank(isolatedPvlan) ? BroadcastDomainType.fromString(isolatedPvlan) : null; + if (isSharedNetworkWithoutSpecifyVlan(ntwkOff) || isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { + bypassVlanOverlapCheck = true; } - } else { - throw new InvalidParameterValueException("Vlan has to be specified; corresponding offering says specifyVlan=true"); - } - } - - if (vlanSpecified) { - URI uri = encodeVlanIdIntoBroadcastUri(vlanId, pNtwk); - // Aux: generate secondary URI for secondary VLAN ID (if provided) for performing checks - URI secondaryUri = StringUtils.isNotBlank(isolatedPvlan) ? BroadcastDomainType.fromString(isolatedPvlan) : null; - if (isSharedNetworkWithoutSpecifyVlan(ntwkOff) || isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { - bypassVlanOverlapCheck = true; - } - //don't allow to specify vlan tag used by physical network for dynamic vlan allocation - if (!(bypassVlanOverlapCheck && (ntwkOff.getGuestType() == GuestType.Shared || isPrivateNetwork)) - && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) { - throw new InvalidParameterValueException("The VLAN tag to use for new guest network, " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " - + zone.getName()); - } - if (secondaryUri != null && !(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) && - _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(secondaryUri)).size() > 0) { - throw new InvalidParameterValueException(String.format( - "The VLAN tag for isolated PVLAN %s is already being used for dynamic vlan allocation for the guest network in zone %s", - isolatedPvlan, zone)); - } - if (!UuidUtils.isUuid(vlanId)) { - // For Isolated and L2 networks, don't allow to create network with vlan that already exists in the zone - if (!hasGuestBypassVlanOverlapCheck(bypassVlanOverlapCheck, ntwkOff, isPrivateNetwork)) { - if (_networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), null).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with vlan %s already exists or overlaps with other network vlans in zone %s", - vlanId, zone)); - } else if (secondaryUri != null && _networksDao.listByZoneAndUriAndGuestType(zoneId, secondaryUri.toString(), null).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with vlan %s already exists or overlaps with other network vlans in zone %s", - isolatedPvlan, zone)); - } else { - final List dcVnets = _datacenterVnetDao.findVnet(zoneId, BroadcastDomainType.getValue(uri)); - //for the network that is created as part of private gateway, - //the vnet is not coming from the data center vnet table, so the list can be empty - if (!dcVnets.isEmpty()) { - final DataCenterVnetVO dcVnet = dcVnets.get(0); - // Fail network creation if specified vlan is dedicated to a different account - if (dcVnet.getAccountGuestVlanMapId() != null) { - final Long accountGuestVlanMapId = dcVnet.getAccountGuestVlanMapId(); - final AccountGuestVlanMapVO map = _accountGuestVlanMapDao.findById(accountGuestVlanMapId); - if (map.getAccountId() != owner.getAccountId()) { - throw new InvalidParameterValueException("Vlan " + vlanId + " is dedicated to a different account"); - } - // Fail network creation if owner has a dedicated range of vlans but the specified vlan belongs to the system pool - } else { - final List maps = _accountGuestVlanMapDao.listAccountGuestVlanMapsByAccount(owner.getAccountId()); - if (maps != null && !maps.isEmpty()) { - final int vnetsAllocatedToAccount = _datacenterVnetDao.countVnetsAllocatedToAccount(zoneId, owner.getAccountId()); - final int vnetsDedicatedToAccount = _datacenterVnetDao.countVnetsDedicatedToAccount(zoneId, owner.getAccountId()); - if (vnetsAllocatedToAccount < vnetsDedicatedToAccount) { - throw new InvalidParameterValueException("Specified vlan " + vlanId + " doesn't belong" + " to the vlan range dedicated to the owner " - + owner.getAccountName()); + //don't allow to specify vlan tag used by physical network for dynamic vlan allocation + if (!(bypassVlanOverlapCheck && (ntwkOff.getGuestType() == GuestType.Shared || isPrivateNetwork)) + && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) { + throw new InvalidParameterValueException("The VLAN tag to use for new guest network, " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + + zone.getName()); + } + if (secondaryUri != null && !(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) && + _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(secondaryUri)).size() > 0) { + throw new InvalidParameterValueException(String.format( + "The VLAN tag for isolated PVLAN %s is already being used for dynamic vlan allocation for the guest network in zone %s", + isolatedPvlan, zone)); + } + if (!UuidUtils.isUuid(vlanId)) { + // For Isolated and L2 networks, don't allow to create network with vlan that already exists in the zone + if (!hasGuestBypassVlanOverlapCheck(bypassVlanOverlapCheck, ntwkOff, isPrivateNetwork)) { + if (_networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), null).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with vlan %s already exists or overlaps with other network vlans in zone %s", + vlanId, zone)); + } else if (secondaryUri != null && _networksDao.listByZoneAndUriAndGuestType(zoneId, secondaryUri.toString(), null).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with vlan %s already exists or overlaps with other network vlans in zone %s", + isolatedPvlan, zone)); + } else { + final List dcVnets = _datacenterVnetDao.findVnet(zoneId, BroadcastDomainType.getValue(uri)); + //for the network that is created as part of private gateway, + //the vnet is not coming from the data center vnet table, so the list can be empty + if (!dcVnets.isEmpty()) { + final DataCenterVnetVO dcVnet = dcVnets.get(0); + // Fail network creation if specified vlan is dedicated to a different account + if (dcVnet.getAccountGuestVlanMapId() != null) { + final Long accountGuestVlanMapId = dcVnet.getAccountGuestVlanMapId(); + final AccountGuestVlanMapVO map = _accountGuestVlanMapDao.findById(accountGuestVlanMapId); + if (map.getAccountId() != owner.getAccountId()) { + throw new InvalidParameterValueException("Vlan " + vlanId + " is dedicated to a different account"); + } + // Fail network creation if owner has a dedicated range of vlans but the specified vlan belongs to the system pool + } else { + final List maps = _accountGuestVlanMapDao.listAccountGuestVlanMapsByAccount(owner.getAccountId()); + if (maps != null && !maps.isEmpty()) { + final int vnetsAllocatedToAccount = _datacenterVnetDao.countVnetsAllocatedToAccount(zoneId, owner.getAccountId()); + final int vnetsDedicatedToAccount = _datacenterVnetDao.countVnetsDedicatedToAccount(zoneId, owner.getAccountId()); + if (vnetsAllocatedToAccount < vnetsDedicatedToAccount) { + throw new InvalidParameterValueException("Specified vlan " + vlanId + " doesn't belong" + " to the vlan range dedicated to the owner " + + owner.getAccountName()); + } } } } } + } else { + // don't allow to creating shared network with given Vlan ID, if there already exists a isolated network or + // shared network with same Vlan ID in the zone + if (!bypassVlanOverlapCheck && _networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), GuestType.Isolated).size() > 0) { + throw new InvalidParameterValueException(String.format( + "There is an existing isolated/shared network that overlaps with vlan id:%s in zone %s", vlanId, zone)); + } } - } else { - // don't allow to creating shared network with given Vlan ID, if there already exists a isolated network or - // shared network with same Vlan ID in the zone - if (!bypassVlanOverlapCheck && _networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), GuestType.Isolated).size() > 0) { + } + + } + + // If networkDomain is not specified, take it from the global configuration + if (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Dns)) { + final Map dnsCapabilities = _networkModel.getNetworkOfferingServiceCapabilities(_entityMgr.findById(NetworkOffering.class, networkOfferingId), + Service.Dns); + final String isUpdateDnsSupported = dnsCapabilities.get(Capability.AllowDnsSuffixModification); + if (isUpdateDnsSupported == null || !Boolean.valueOf(isUpdateDnsSupported)) { + if (networkDomain != null) { + // TBD: NetworkOfferingId and zoneId. Send uuids instead. throw new InvalidParameterValueException(String.format( - "There is an existing isolated/shared network that overlaps with vlan id:%s in zone %s", vlanId, zone)); + "Domain name change is not supported by network offering id=%d in zone %s", + networkOfferingId, zone)); } - } - } - - } - - // If networkDomain is not specified, take it from the global configuration - if (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Dns)) { - final Map dnsCapabilities = _networkModel.getNetworkOfferingServiceCapabilities(_entityMgr.findById(NetworkOffering.class, networkOfferingId), - Service.Dns); - final String isUpdateDnsSupported = dnsCapabilities.get(Capability.AllowDnsSuffixModification); - if (isUpdateDnsSupported == null || !Boolean.valueOf(isUpdateDnsSupported)) { - if (networkDomain != null) { - // TBD: NetworkOfferingId and zoneId. Send uuids instead. - throw new InvalidParameterValueException(String.format( - "Domain name change is not supported by network offering id=%d in zone %s", - networkOfferingId, zone)); - } - } else { - if (networkDomain == null) { - // 1) Get networkDomain from the corresponding account/domain/zone - if (aclType == ACLType.Domain) { - networkDomain = _networkModel.getDomainNetworkDomain(domainId, zoneId); - } else if (aclType == ACLType.Account) { - networkDomain = _networkModel.getAccountNetworkDomain(owner.getId(), zoneId); - } - - // 2) If null, generate networkDomain using domain suffix from the global config variables - if (networkDomain == null) { - networkDomain = "cs" + Long.toHexString(owner.getId()) + GuestDomainSuffix.valueIn(zoneId); - } - } else { - // validate network domain - if (!NetUtils.verifyDomainName(networkDomain)) { - throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " - + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + if (networkDomain == null) { + // 1) Get networkDomain from the corresponding account/domain/zone + if (aclType == ACLType.Domain) { + networkDomain = _networkModel.getDomainNetworkDomain(domainId, zoneId); + } else if (aclType == ACLType.Account) { + networkDomain = _networkModel.getAccountNetworkDomain(owner.getId(), zoneId); + } + + // 2) If null, generate networkDomain using domain suffix from the global config variables + if (networkDomain == null) { + networkDomain = "cs" + Long.toHexString(owner.getId()) + GuestDomainSuffix.valueIn(zoneId); + } + + } else { + // validate network domain + if (!NetUtils.verifyDomainName(networkDomain)) { + throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " + + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); + } } } } - } - // In Advance zone Cidr for Shared networks and Isolated networks w/o source nat service can't be NULL - 2.2.x - // limitation, remove after we introduce support for multiple ip ranges - // with different Cidrs for the same Shared network - final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced - && ntwkOff.getTrafficType() == TrafficType.Guest - && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated - && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat) - && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.Gateway))); - if (cidr == null && ip6Cidr == null && cidrRequired) { - if (ntwkOff.getGuestType() == GuestType.Shared) { - throw new InvalidParameterValueException(String.format("Gateway/netmask are required when creating %s networks.", Network.GuestType.Shared)); - } else { - throw new InvalidParameterValueException("gateway/netmask are required when create network of" + " type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled"); + // In Advance zone Cidr for Shared networks and Isolated networks w/o source nat service can't be NULL - 2.2.x + // limitation, remove after we introduce support for multiple ip ranges + // with different Cidrs for the same Shared network + final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced + && ntwkOff.getTrafficType() == TrafficType.Guest + && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated + && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat) + && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.Gateway))); + if (cidr == null && ip6Cidr == null && cidrRequired) { + if (ntwkOff.getGuestType() == GuestType.Shared) { + throw new InvalidParameterValueException(String.format("Gateway/netmask are required when creating %s networks.", Network.GuestType.Shared)); + } else { + throw new InvalidParameterValueException("gateway/netmask are required when create network of" + " type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled"); + } } - } - checkL2OfferingServices(ntwkOff); + checkL2OfferingServices(ntwkOff); - // No cidr can be specified in Basic zone - if (zone.getNetworkType() == NetworkType.Basic && cidr != null) { - throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic); - } + // No cidr can be specified in Basic zone + if (zone.getNetworkType() == NetworkType.Basic && cidr != null) { + throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic); + } - // Check if cidr is RFC1918 compliant if the network is Guest Isolated for IPv4 - if (cidr != null && (ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) && - !NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { + // Check if cidr is RFC1918 compliant if the network is Guest Isolated for IPv4 + if (cidr != null && (ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) && + !NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { throw new InvalidParameterValueException("Virtual Guest Cidr " + cidr + " is not RFC 1918 or 6598 compliant"); - } + } final String networkDomainFinal = networkDomain; final String vlanIdFinal = vlanId; @@ -3004,75 +3003,75 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra final NetworkVO userNetwork = new NetworkVO(); userNetwork.setNetworkDomain(networkDomainFinal); - if (cidr != null && gateway != null) { - userNetwork.setCidr(cidr); - userNetwork.setGateway(gateway); - } + if (cidr != null && gateway != null) { + userNetwork.setCidr(cidr); + userNetwork.setGateway(gateway); + } - if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { - userNetwork.setIp6Cidr(ip6Cidr); - userNetwork.setIp6Gateway(ip6Gateway); - } + if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { + userNetwork.setIp6Cidr(ip6Cidr); + userNetwork.setIp6Gateway(ip6Gateway); + } - if (externalId != null) { - userNetwork.setExternalId(externalId); - } + if (externalId != null) { + userNetwork.setExternalId(externalId); + } - if (StringUtils.isNotBlank(routerIp)) { - userNetwork.setRouterIp(routerIp); - } + if (StringUtils.isNotBlank(routerIp)) { + userNetwork.setRouterIp(routerIp); + } - if (StringUtils.isNotBlank(routerIpv6)) { - userNetwork.setRouterIpv6(routerIpv6); - } + if (StringUtils.isNotBlank(routerIpv6)) { + userNetwork.setRouterIpv6(routerIpv6); + } - if (vrIfaceMTUs != null) { - if (vrIfaceMTUs.first() != null && vrIfaceMTUs.first() > 0) { - userNetwork.setPublicMtu(vrIfaceMTUs.first()); + if (vrIfaceMTUs != null) { + if (vrIfaceMTUs.first() != null && vrIfaceMTUs.first() > 0) { + userNetwork.setPublicMtu(vrIfaceMTUs.first()); + } else { + userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); + } + + if (vrIfaceMTUs.second() != null && vrIfaceMTUs.second() > 0) { + userNetwork.setPrivateMtu(vrIfaceMTUs.second()); + } else { + userNetwork.setPrivateMtu(Integer.valueOf(NetworkService.VRPrivateInterfaceMtu.defaultValue())); + } } else { userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); - } - - if (vrIfaceMTUs.second() != null && vrIfaceMTUs.second() > 0) { - userNetwork.setPrivateMtu(vrIfaceMTUs.second()); - } else { userNetwork.setPrivateMtu(Integer.valueOf(NetworkService.VRPrivateInterfaceMtu.defaultValue())); } - } else { - userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); - userNetwork.setPrivateMtu(Integer.valueOf(NetworkService.VRPrivateInterfaceMtu.defaultValue())); - } - if (!GuestType.L2.equals(userNetwork.getGuestType())) { - if (StringUtils.isNotBlank(ip4Dns1)) { - userNetwork.setDns1(ip4Dns1); - } - if (StringUtils.isNotBlank(ip4Dns2)) { - userNetwork.setDns2(ip4Dns2); - } - if (StringUtils.isNotBlank(ip6Dns1)) { - userNetwork.setIp6Dns1(ip6Dns1); - } - if (StringUtils.isNotBlank(ip6Dns2)) { - userNetwork.setIp6Dns2(ip6Dns2); - } - } - - if (vlanIdFinal != null) { - if (isolatedPvlan == null) { - URI uri = null; - if (UuidUtils.isUuid(vlanIdFinal)) { - //Logical router's UUID provided as VLAN_ID - userNetwork.setVlanIdAsUUID(vlanIdFinal); //Set transient field - } else { - uri = encodeVlanIdIntoBroadcastUri(vlanIdFinal, pNtwk); + if (!GuestType.L2.equals(userNetwork.getGuestType())) { + if (StringUtils.isNotBlank(ip4Dns1)) { + userNetwork.setDns1(ip4Dns1); } - - if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString()).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with vlan %s already exists or overlaps with other network pvlans in zone %s", - vlanIdFinal, zone)); + if (StringUtils.isNotBlank(ip4Dns2)) { + userNetwork.setDns2(ip4Dns2); } + if (StringUtils.isNotBlank(ip6Dns1)) { + userNetwork.setIp6Dns1(ip6Dns1); + } + if (StringUtils.isNotBlank(ip6Dns2)) { + userNetwork.setIp6Dns2(ip6Dns2); + } + } + + if (vlanIdFinal != null) { + if (isolatedPvlan == null) { + URI uri = null; + if (UuidUtils.isUuid(vlanIdFinal)) { + //Logical router's UUID provided as VLAN_ID + userNetwork.setVlanIdAsUUID(vlanIdFinal); //Set transient field + } else { + uri = encodeVlanIdIntoBroadcastUri(vlanIdFinal, pNtwk); + } + + if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString()).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with vlan %s already exists or overlaps with other network pvlans in zone %s", + vlanIdFinal, zone)); + } userNetwork.setBroadcastUri(uri); if (!vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) { diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java index 445a59310fb..377b2f91375 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.upgrade; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -96,7 +97,9 @@ public final class DatabaseVersionHierarchy { // we cannot find the version specified, so get the // most recent one immediately before this version if (!contains(fromVersion)) { - return getPath(getRecentVersion(fromVersion), toVersion); + DbUpgrade[] dbUpgrades = getPath(getRecentVersion(fromVersion), toVersion); + return Arrays.stream(dbUpgrades).filter(up -> CloudStackVersion.compare(up.getUpgradedVersion(), fromVersion.toString()) > 0) + .toArray(DbUpgrade[]::new); } final Predicate predicate; diff --git a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java index d8cf070ae4c..89b71f03289 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java @@ -315,11 +315,11 @@ public class SystemVmTemplateRegistration { public static final List> hypervisorList = Arrays.asList( new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), - new Pair<>(Hypervisor.HypervisorType.VMware, null), - new Pair<>(Hypervisor.HypervisorType.XenServer, null), - new Pair<>(Hypervisor.HypervisorType.Hyperv, null), - new Pair<>(Hypervisor.HypervisorType.LXC, null), - new Pair<>(Hypervisor.HypervisorType.Ovm3, null) + new Pair<>(Hypervisor.HypervisorType.VMware, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.XenServer, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.Hyperv, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.LXC, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.Ovm3, CPU.CPUArch.amd64) ); public static final Map NewTemplateMap = new HashMap<>(); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java index 68100e16401..e0aa38a717b 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java @@ -57,8 +57,4 @@ public class Upgrade42020to42030 extends DbUpgradeAbstractImpl implements DbUpgr public InputStream[] getCleanupScripts() { return null; } - - @Override - public void updateSystemVmTemplates(Connection conn) { - } } diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql index 3dd6c18f57c..4405250f271 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql @@ -31,7 +31,7 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account', 'api_key_access', 'boolean CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" '); -- Create a new group for Usage Server related configurations -INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Usage Server', 'Usage Server related configuration', 9); +INSERT IGNORE INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Usage Server', 'Usage Server related configuration', 9); UPDATE `cloud`.`configuration_subgroup` set `group_id` = (SELECT `id` FROM `cloud`.`configuration_group` WHERE `name` = 'Usage Server'), `precedence` = 1 WHERE `name`='Usage'; UPDATE `cloud`.`configuration` SET `group_id` = (SELECT `id` FROM `cloud`.`configuration_group` WHERE `name` = 'Usage Server') where `subgroup_id` = (SELECT `id` FROM `cloud`.`configuration_subgroup` WHERE `name` = 'Usage'); diff --git a/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java index 27995eb179a..ab64e4698f0 100644 --- a/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java +++ b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java @@ -44,6 +44,7 @@ import com.cloud.upgrade.dao.Upgrade41120to41130; import com.cloud.upgrade.dao.Upgrade41120to41200; import com.cloud.upgrade.dao.Upgrade41510to41520; import com.cloud.upgrade.dao.Upgrade41610to41700; +import com.cloud.upgrade.dao.Upgrade42010to42100; import com.cloud.upgrade.dao.Upgrade452to453; import com.cloud.upgrade.dao.Upgrade453to460; import com.cloud.upgrade.dao.Upgrade460to461; @@ -380,4 +381,23 @@ public class DatabaseUpgradeCheckerTest { assertFalse("DatabaseUpgradeChecker should not be a standalone component", checker.isStandalone()); } + @Test + public void testCalculateUpgradePath42010to42100() { + + final CloudStackVersion dbVersion = CloudStackVersion.parse("4.20.1.0"); + assertNotNull(dbVersion); + + final CloudStackVersion currentVersion = CloudStackVersion.parse("4.21.0.0"); + assertNotNull(currentVersion); + + final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker(); + final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion); + + assertNotNull(upgrades); + assertEquals(1, upgrades.length); + assertTrue(upgrades[0] instanceof Upgrade42010to42100); + + assertArrayEquals(new String[]{"4.20.1.0", "4.21.0.0"}, upgrades[0].getUpgradableVersionRange()); + assertEquals(currentVersion.toString(), upgrades[0].getUpgradedVersion()); + } } diff --git a/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java index b943f48ad36..93be850f558 100644 --- a/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java +++ b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java @@ -155,7 +155,7 @@ public class SystemVmTemplateRegistrationTest { templateDetails = SystemVmTemplateRegistration.NewTemplateMap.get("vmware"); assertNotNull(templateDetails); - assertNull(templateDetails.getArch()); + assertEquals(CPU.CPUArch.amd64, templateDetails.getArch()); assertEquals(Hypervisor.HypervisorType.VMware, templateDetails.getHypervisorType()); } diff --git a/plugins/hypervisors/baremetal/pom.xml b/plugins/hypervisors/baremetal/pom.xml index 384090e0450..bfdf58884a2 100755 --- a/plugins/hypervisors/baremetal/pom.xml +++ b/plugins/hypervisors/baremetal/pom.xml @@ -1,51 +1,51 @@ - - - 4.0.0 - - org.apache.cloudstack - cloudstack-plugins - 4.22.1.0-SNAPSHOT - ../../pom.xml - - cloud-plugin-hypervisor-baremetal - Apache CloudStack Plugin - Hypervisor Baremetal - - - commons-lang - commons-lang - - - javax.xml.bind - jaxb-api - ${cs.jaxb.version} - - - com.sun.xml.bind - jaxb-core - ${cs.jaxb.version} - - - com.sun.xml.bind - jaxb-impl - ${cs.jaxb.impl.version} - - - + + + 4.0.0 + + org.apache.cloudstack + cloudstack-plugins + 4.22.1.0-SNAPSHOT + ../../pom.xml + + cloud-plugin-hypervisor-baremetal + Apache CloudStack Plugin - Hypervisor Baremetal + + + commons-lang + commons-lang + + + javax.xml.bind + jaxb-api + ${cs.jaxb.version} + + + com.sun.xml.bind + jaxb-core + ${cs.jaxb.version} + + + com.sun.xml.bind + jaxb-impl + ${cs.jaxb.impl.version} + + + diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index cf69234d19e..55924cb32e8 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -548,7 +548,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu List firewallRules = firewallRulesDao.listByIpPurposeProtocolAndNotRevoked(publicIp.getId(), FirewallRule.Purpose.Firewall, NetUtils.TCP_PROTO); for (FirewallRuleVO firewallRule : firewallRules) { PortForwardingRuleVO pfRule = portForwardingRulesDao.findByNetworkAndPorts(networkId, firewallRule.getSourcePortStart(), firewallRule.getSourcePortEnd()); - if (firewallRule.getSourcePortStart() == CLUSTER_NODES_DEFAULT_START_SSH_PORT || (Objects.nonNull(pfRule) && pfRule.getDestinationPortStart() == DEFAULT_SSH_PORT) ) { + if (Objects.equals(firewallRule.getSourcePortStart(), CLUSTER_NODES_DEFAULT_START_SSH_PORT) || (Objects.nonNull(pfRule) && pfRule.getDestinationPortStart() == DEFAULT_SSH_PORT) ) { rule = firewallRule; firewallService.revokeIngressFwRule(firewallRule.getId(), true); logger.debug("The SSH firewall rule {} with the id {} was revoked", firewallRule.getName(), firewallRule.getId()); diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java index 6d22a1a3a03..0ca27c29d8d 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java @@ -139,10 +139,14 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif // Remove existing SSH firewall rules FirewallRule firewallRule = removeSshFirewallRule(publicIp, network.getId()); + int existingFirewallRuleSourcePortEnd; if (firewallRule == null) { - throw new ManagementServerException("Firewall rule for node SSH access can't be provisioned"); + logger.warn("SSH firewall rule not found for Kubernetes cluster: {}. It may have been manually deleted or modified.", kubernetesCluster.getName()); + existingFirewallRuleSourcePortEnd = CLUSTER_NODES_DEFAULT_START_SSH_PORT + clusterVMIds.size() - 1; + } else { + existingFirewallRuleSourcePortEnd = firewallRule.getSourcePortEnd(); } - int existingFirewallRuleSourcePortEnd = firewallRule.getSourcePortEnd(); + try { removePortForwardingRules(publicIp, network, owner, CLUSTER_NODES_DEFAULT_START_SSH_PORT, existingFirewallRuleSourcePortEnd); } catch (ResourceUnavailableException e) { diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index b610867275e..6575da47364 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -1159,21 +1159,19 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C long reservedIpAddressesAmount = ipDedicatedAccountId == null ? 1L : 0L; try (CheckedReservation publicIpAddressReservation = new CheckedReservation(account, Resource.ResourceType.public_ip, reservedIpAddressesAmount, reservationDao, _resourceLimitMgr)) { - - List maps = _accountVlanMapDao.listAccountVlanMapsByVlan(ipVO.getVlanId()); - ipVO.setAllocatedTime(new Date()); - ipVO.setAllocatedToAccountId(account.getAccountId()); - ipVO.setAllocatedInDomainId(account.getDomainId()); - ipVO.setState(State.Reserved); - if (displayIp != null) { - ipVO.setDisplay(displayIp); - } - ipVO = _ipAddressDao.persist(ipVO); - if (reservedIpAddressesAmount > 0) { - _resourceLimitMgr.incrementResourceCount(account.getId(), Resource.ResourceType.public_ip); - } - return ipVO; - + List maps = _accountVlanMapDao.listAccountVlanMapsByVlan(ipVO.getVlanId()); + ipVO.setAllocatedTime(new Date()); + ipVO.setAllocatedToAccountId(account.getAccountId()); + ipVO.setAllocatedInDomainId(account.getDomainId()); + ipVO.setState(State.Reserved); + if (displayIp != null) { + ipVO.setDisplay(displayIp); + } + ipVO = _ipAddressDao.persist(ipVO); + if (reservedIpAddressesAmount > 0) { + _resourceLimitMgr.incrementResourceCount(account.getId(), Resource.ResourceType.public_ip); + } + return ipVO; } catch (ResourceAllocationException ex) { logger.warn("Failed to allocate resource of type " + ex.getResourceType() + " for account " + account); throw new AccountLimitException("Maximum number of public IP addresses for account: " + account.getAccountName() + " has been exceeded."); diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java index d798afc644a..5717f8745ac 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -1317,25 +1317,25 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis vpc.setUseRouterIpResolver(Boolean.TRUE.equals(useVrIpResolver)); try (CheckedReservation vpcReservation = new CheckedReservation(owner, ResourceType.vpc, null, null, 1L, reservationDao, _resourceLimitMgr)) { - if (vpc.getCidr() == null && cidrSize != null) { - // Allocate a CIDR for VPC - Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForVpc(vpc, cidrSize); - if (subnet != null) { - vpc.setCidr(subnet.getSubnet()); - } else { - throw new CloudRuntimeException("Failed to allocate a CIDR with requested size for VPC."); + if (vpc.getCidr() == null && cidrSize != null) { + // Allocate a CIDR for VPC + Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForVpc(vpc, cidrSize); + if (subnet != null) { + vpc.setCidr(subnet.getSubnet()); + } else { + throw new CloudRuntimeException("Failed to allocate a CIDR with requested size for VPC."); + } } - } - Vpc newVpc = createVpc(displayVpc, vpc); - // assign Ipv4 subnet to Routed VPC - if (routedIpv4Manager.isRoutedVpc(vpc)) { - routedIpv4Manager.assignIpv4SubnetToVpc(newVpc); - } - if (CollectionUtils.isNotEmpty(bgpPeerIds)) { - routedIpv4Manager.persistBgpPeersForVpc(newVpc.getId(), bgpPeerIds); - } - return newVpc; + Vpc newVpc = createVpc(displayVpc, vpc); + // assign Ipv4 subnet to Routed VPC + if (routedIpv4Manager.isRoutedVpc(vpc)) { + routedIpv4Manager.assignIpv4SubnetToVpc(newVpc); + } + if (CollectionUtils.isNotEmpty(bgpPeerIds)) { + routedIpv4Manager.persistBgpPeersForVpc(newVpc.getId(), bgpPeerIds); + } + return newVpc; } } diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java index c3fa2fab02e..d165f0cd1b6 100644 --- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java @@ -277,40 +277,39 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C } try (CheckedReservation projectReservation = new CheckedReservation(owner, ResourceType.project, null, null, 1L, reservationDao, _resourceLimitMgr)) { + final Account ownerFinal = owner; + User finalUser = user; + Project project = Transaction.execute(new TransactionCallback() { + @Override + public Project doInTransaction(TransactionStatus status) { - final Account ownerFinal = owner; - User finalUser = user; - Project project = Transaction.execute(new TransactionCallback() { - @Override - public Project doInTransaction(TransactionStatus status) { + //Create an account associated with the project + StringBuilder acctNm = new StringBuilder("PrjAcct-"); + acctNm.append(name).append("-").append(ownerFinal.getDomainId()); - //Create an account associated with the project - StringBuilder acctNm = new StringBuilder("PrjAcct-"); - acctNm.append(name).append("-").append(ownerFinal.getDomainId()); + Account projectAccount = _accountMgr.createAccount(acctNm.toString(), Account.Type.PROJECT, null, domainId, null, null, UUID.randomUUID().toString()); - Account projectAccount = _accountMgr.createAccount(acctNm.toString(), Account.Type.PROJECT, null, domainId, null, null, UUID.randomUUID().toString()); + Project project = _projectDao.persist(new ProjectVO(name, displayText, ownerFinal.getDomainId(), projectAccount.getId())); - Project project = _projectDao.persist(new ProjectVO(name, displayText, ownerFinal.getDomainId(), projectAccount.getId())); + //assign owner to the project + assignAccountToProject(project, ownerFinal.getId(), ProjectAccount.Role.Admin, + Optional.ofNullable(finalUser).map(User::getId).orElse(null), null); - //assign owner to the project - assignAccountToProject(project, ownerFinal.getId(), ProjectAccount.Role.Admin, - Optional.ofNullable(finalUser).map(User::getId).orElse(null), null); + if (project != null) { + CallContext.current().setEventDetails("Project id=" + project.getId()); + CallContext.current().putContextParameter(Project.class, project.getUuid()); + } - if (project != null) { - CallContext.current().setEventDetails("Project id=" + project.getId()); - CallContext.current().putContextParameter(Project.class, project.getUuid()); + //Increment resource count + _resourceLimitMgr.incrementResourceCount(ownerFinal.getId(), ResourceType.project); + + return project; } + }); - //Increment resource count - _resourceLimitMgr.incrementResourceCount(ownerFinal.getId(), ResourceType.project); + messageBus.publish(_name, ProjectManager.MESSAGE_CREATE_TUNGSTEN_PROJECT_EVENT, PublishScope.LOCAL, project); - return project; - } - }); - - messageBus.publish(_name, ProjectManager.MESSAGE_CREATE_TUNGSTEN_PROJECT_EVENT, PublishScope.LOCAL, project); - - return project; + return project; } } @@ -604,16 +603,16 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C boolean shouldIncrementResourceCount = projectRole != null && Role.Admin == projectRole; try (CheckedReservation cr = new CheckedReservation(userAccount, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) { - if (assignUserToProject(project, user.getId(), user.getAccountId(), projectRole, - Optional.ofNullable(role).map(ProjectRole::getId).orElse(null)) != null) { - if (shouldIncrementResourceCount) { - _resourceLimitMgr.incrementResourceCount(userAccount.getId(), ResourceType.project); + if (assignUserToProject(project, user.getId(), user.getAccountId(), projectRole, + Optional.ofNullable(role).map(ProjectRole::getId).orElse(null)) != null) { + if (shouldIncrementResourceCount) { + _resourceLimitMgr.incrementResourceCount(userAccount.getId(), ResourceType.project); + } + return true; + } else { + logger.warn("Failed to add user to project: {}", project); + return false; } - return true; - } else { - logger.warn("Failed to add user to project: {}", project); - return false; - } } } } @@ -671,13 +670,13 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C boolean shouldIncrementResourceCount = Role.Admin == newAccRole; try (CheckedReservation checkedReservation = new CheckedReservation(account, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) { - futureOwner.setAccountRole(newAccRole); - _projectAccountDao.update(futureOwner.getId(), futureOwner); - if (shouldIncrementResourceCount) { - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.project); - } else { - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.project); - } + futureOwner.setAccountRole(newAccRole); + _projectAccountDao.update(futureOwner.getId(), futureOwner); + if (shouldIncrementResourceCount) { + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.project); + } else { + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.project); + } } } @@ -721,16 +720,16 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C } try (CheckedReservation checkedReservation = new CheckedReservation(futureOwnerAccount, ResourceType.project, null, null, 1L, reservationDao, _resourceLimitMgr)) { - //unset the role for the old owner - ProjectAccountVO currentOwner = _projectAccountDao.findByProjectIdAccountId(projectId, currentOwnerAccount.getId()); - currentOwner.setAccountRole(Role.Regular); - _projectAccountDao.update(currentOwner.getId(), currentOwner); - _resourceLimitMgr.decrementResourceCount(currentOwnerAccount.getId(), ResourceType.project); + //unset the role for the old owner + ProjectAccountVO currentOwner = _projectAccountDao.findByProjectIdAccountId(projectId, currentOwnerAccount.getId()); + currentOwner.setAccountRole(Role.Regular); + _projectAccountDao.update(currentOwner.getId(), currentOwner); + _resourceLimitMgr.decrementResourceCount(currentOwnerAccount.getId(), ResourceType.project); - //set new owner - futureOwner.setAccountRole(Role.Admin); - _projectAccountDao.update(futureOwner.getId(), futureOwner); - _resourceLimitMgr.incrementResourceCount(futureOwnerAccount.getId(), ResourceType.project); + //set new owner + futureOwner.setAccountRole(Role.Admin); + _projectAccountDao.update(futureOwner.getId(), futureOwner); + _resourceLimitMgr.incrementResourceCount(futureOwnerAccount.getId(), ResourceType.project); } } else { logger.trace("Future owner {}is already the owner of the project {}", newOwnerName, project); @@ -877,16 +876,16 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C boolean shouldIncrementResourceCount = projectRoleType != null && Role.Admin == projectRoleType; try (CheckedReservation cr = new CheckedReservation(account, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) { - if (assignAccountToProject(project, account.getId(), projectRoleType, null, - Optional.ofNullable(projectRole).map(ProjectRole::getId).orElse(null)) != null) { - if (shouldIncrementResourceCount) { - _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.project); + if (assignAccountToProject(project, account.getId(), projectRoleType, null, + Optional.ofNullable(projectRole).map(ProjectRole::getId).orElse(null)) != null) { + if (shouldIncrementResourceCount) { + _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.project); + } + return true; + } else { + logger.warn("Failed to add account {} to project {}", accountName, project); + return false; } - return true; - } else { - logger.warn("Failed to add account {} to project {}", accountName, project); - return false; - } } } } diff --git a/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java b/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java index 5c6f8dc6599..c670b631645 100755 --- a/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java +++ b/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java @@ -361,7 +361,7 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto boolean success = true; Long currentSize = answer.getVirtualSize() != 0 ? answer.getVirtualSize() : answer.getPhysicalSize(); Long lastSize = volume.getSize() != null ? volume.getSize() : 0L; - if (!checkAndUpdateSecondaryStorageResourceLimit(volume.getAccountId(), volume.getSize(), currentSize)) { + if (!checkAndUpdateSecondaryStorageResourceLimit(volume.getAccountId(), lastSize, currentSize)) { volumeDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR); volumeDataStore.setState(State.Failed); volumeDataStore.setErrorString("Storage Limit Reached"); diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 9f9928bfb66..2d170049663 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.storage; +import static com.cloud.configuration.ConfigurationManagerImpl.SystemVMUseLocalStorage; import static com.cloud.utils.NumbersUtil.toHumanReadableSize; import java.io.UnsupportedEncodingException; @@ -152,6 +153,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.time.DateUtils; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.EnumUtils; import org.springframework.stereotype.Component; @@ -184,7 +186,6 @@ import com.cloud.capacity.dao.CapacityDao; import com.cloud.cluster.ClusterManagerListener; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; -import com.cloud.configuration.ConfigurationManagerImpl; import com.cloud.configuration.Resource.ResourceType; import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; @@ -819,6 +820,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return createLocalStorage(host, pInfo); } + private boolean isLocalStorageEnabledForZone(DataCenterVO zone) { + return zone.isLocalStorageEnabled() || BooleanUtils.toBoolean(SystemVMUseLocalStorage.valueIn(zone.getId())); + } + @DB @Override public DataStore createLocalStorage(Host host, StoragePoolInfo pInfo) throws ConnectionException { @@ -826,12 +831,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C if (dc == null) { return null; } - boolean useLocalStorageForSystemVM = false; - Boolean isLocal = ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dc.getId()); - if (isLocal != null) { - useLocalStorageForSystemVM = isLocal.booleanValue(); - } - if (!(dc.isLocalStorageEnabled() || useLocalStorageForSystemVM)) { + if (!isLocalStorageEnabledForZone(dc)) { return null; } DataStore store = null; @@ -1033,6 +1033,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(account.getId())) { throw new PermissionDeniedException(String.format("Cannot perform this operation, Zone is currently disabled: %s", zone)); } + // Check if it's local storage and if it's enabled on the zone + if (isFileScheme && !isLocalStorageEnabledForZone(zone)) { + throw new InvalidParameterValueException("Local storage is not enabled for zone: " + zone); + } managementService.checkJsInterpretationAllowedIfNeededForParameterValue(ApiConstants.IS_TAG_A_RULE, Boolean.TRUE.equals(cmd.isTagARule())); diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index a92922aeff9..687e432f166 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -2761,13 +2761,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic try (CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, resourceLimitStorageTags, requiredPrimaryStorageSpace, reservationDao, _resourceLimitMgr)) { - _jobMgr.updateAsyncJobAttachment(job.getId(), "Volume", volumeId); + _jobMgr.updateAsyncJobAttachment(job.getId(), "Volume", volumeId); - if (asyncExecutionContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { - return safelyOrchestrateAttachVolume(vmId, volumeId, deviceId); - } else { - return getVolumeAttachJobResult(vmId, volumeId, deviceId); - } + if (asyncExecutionContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + return safelyOrchestrateAttachVolume(vmId, volumeId, deviceId); + } else { + return getVolumeAttachJobResult(vmId, volumeId, deviceId); + } } catch (ResourceAllocationException e) { logger.error("primary storage resource limit check failed", e); @@ -4400,12 +4400,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic try { _resourceLimitMgr.checkVolumeResourceLimit(newAccount, true, volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId()), reservations); - Transaction.execute(new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - updateVolumeAccount(oldAccount, volume, newAccount); - } - }); + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + updateVolumeAccount(oldAccount, volume, newAccount); + } + }); return volume; diff --git a/server/src/main/java/com/cloud/storage/download/DownloadListener.java b/server/src/main/java/com/cloud/storage/download/DownloadListener.java index 058881fdb54..a35f4391b0a 100644 --- a/server/src/main/java/com/cloud/storage/download/DownloadListener.java +++ b/server/src/main/java/com/cloud/storage/download/DownloadListener.java @@ -280,7 +280,7 @@ public class DownloadListener implements Listener { } private Long getSizeFromDB() { - Long lastSize = 0L; + Long lastSize = null; if (DataObjectType.TEMPLATE.equals(object.getType())) { TemplateDataStoreVO t = _templateDataStoreDao.findByStoreTemplate(object.getDataStore().getId(), object.getId()); lastSize = t.getSize(); @@ -288,7 +288,7 @@ public class DownloadListener implements Listener { VolumeVO v = _volumeDao.findById(object.getId()); lastSize = v.getSize(); } - return lastSize; + return lastSize == null ? 0L : lastSize; } private Boolean checkAndUpdateResourceLimits(DownloadAnswer answer) { diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 2f8f7b58de1..d60bff09540 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -35,7 +35,6 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.acl.SecurityChecker; -import com.cloud.api.ApiDBUtils; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -94,6 +93,7 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.DeleteSnapshotsDirCommand; import com.cloud.alert.AlertManager; +import com.cloud.api.ApiDBUtils; import com.cloud.api.commands.ListRecurringSnapshotScheduleCmd; import com.cloud.api.query.MutualExclusiveIdsManagerBase; import com.cloud.configuration.Config; @@ -2048,17 +2048,17 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement try (CheckedReservation volumeSnapshotReservation = new CheckedReservation(owner, ResourceType.snapshot, null, null, 1L, reservationDao, _resourceLimitMgr); CheckedReservation storageReservation = new CheckedReservation(owner, storeResourceType, null, null, volume.getSize(), reservationDao, _resourceLimitMgr)) { - SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName, - (short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), volume.getMinIops(), volume.getMaxIops(), hypervisorType, locationType); + SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName, + (short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), volume.getMinIops(), volume.getMaxIops(), hypervisorType, locationType); - SnapshotVO snapshot = _snapshotDao.persist(snapshotVO); - if (snapshot == null) { - throw new CloudRuntimeException(String.format("Failed to create snapshot for volume: %s", volume)); - } - CallContext.current().putContextParameter(Snapshot.class, snapshot.getUuid()); - _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.snapshot); - _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), storeResourceType, volume.getSize()); - return snapshot; + SnapshotVO snapshot = _snapshotDao.persist(snapshotVO); + if (snapshot == null) { + throw new CloudRuntimeException(String.format("Failed to create snapshot for volume: %s", volume)); + } + CallContext.current().putContextParameter(Snapshot.class, snapshot.getUuid()); + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.snapshot); + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), storeResourceType, volume.getSize()); + return snapshot; } catch (ResourceAllocationException e) { if (snapshotType != Type.MANUAL) { String msg = String.format("Snapshot resource limit exceeded for account id : %s. Failed to create recurring snapshots", owner.getId()); diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index 25fb2c300dd..c0e45a75f81 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -375,10 +375,10 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, if (template != null) { CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid()); return template; - } else { - throw new CloudRuntimeException("Failed to create ISO"); } } + + throw new CloudRuntimeException("Failed to create ISO"); } @Override @@ -407,7 +407,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, TemplateProfile profile = adapter.prepare(cmd); VMTemplateVO template = adapter.create(profile); - // Secondary storage resource usage will be recalculated in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack + // Secondary storage resource usage will be incremented in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack // for HypervisorTemplateAdapter _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); if (secondaryStorageUsage > 0) { @@ -420,10 +420,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, vnfTemplateManager.persistVnfTemplate(template.getId(), (RegisterVnfTemplateCmd) cmd); } return template; - } else { - throw new CloudRuntimeException("Failed to create a Template"); } } + throw new CloudRuntimeException("Failed to create a Template"); } /** @@ -1034,12 +1033,14 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, logger.debug("There is Template {} in secondary storage {} in zone {} , don't need to copy", template, dstSecStore, dataCenterVOs.get(destZoneId)); continue; } - try (CheckedReservation secondaryStorageReservation = new CheckedReservation(templateOwner, ResourceType.secondary_storage, null, null, template.getSize(), reservationDao, _resourceLimitMgr)) { - if (!copy(userId, template, srcSecStore, dataCenterVOs.get(destZoneId))) { - failedZones.add(dataCenterVOs.get(destZoneId).getName()); - continue; + if (template.getSize() != null) { + try (CheckedReservation secondaryStorageReservation = new CheckedReservation(templateOwner, ResourceType.secondary_storage, null, null, template.getSize(), reservationDao, _resourceLimitMgr)) { + if (!copy(userId, template, srcSecStore, dataCenterVOs.get(destZoneId))) { + failedZones.add(dataCenterVOs.get(destZoneId).getName()); + continue; + } + _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, template.getSize()); } - _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, template.getSize()); } } } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index dbc28cb2d7c..fe18263fd3b 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -60,7 +60,6 @@ import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; -import com.cloud.resourcelimit.ReservationHelper; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -314,6 +313,7 @@ import com.cloud.org.Grouping; import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceState; import com.cloud.resourcelimit.CheckedReservation; +import com.cloud.resourcelimit.ReservationHelper; import com.cloud.server.ManagementService; import com.cloud.server.ResourceTag; import com.cloud.server.StatsCollector; @@ -1325,46 +1325,45 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public void validateCustomParameters(ServiceOfferingVO serviceOffering, Map customParameters) { - //TODO need to validate custom cpu, and memory against min/max CPU/Memory ranges from service_offering_details table - if (customParameters.size() != 0) { - Map offeringDetails = serviceOfferingDetailsDao.listDetailsKeyPairs(serviceOffering.getId()); - if (serviceOffering.getCpu() == null) { - int minCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_CPU_NUMBER), 1); - int maxCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_CPU_NUMBER), Integer.MAX_VALUE); - int cpuNumber = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name()), -1); - Integer maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value(); - if (cpuNumber < minCPU || cpuNumber > maxCPU || cpuNumber > maxCPUCores) { - throw new InvalidParameterValueException(String.format("Invalid CPU cores value, specify a value between %d and %d", minCPU, Math.min(maxCPUCores, maxCPU))); - } - } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) { - throw new InvalidParameterValueException("The CPU cores of this offering id:" + serviceOffering.getUuid() - + " is not customizable. This is predefined in the Template."); - } - - if (serviceOffering.getSpeed() == null) { - String cpuSpeed = customParameters.get(UsageEventVO.DynamicParameters.cpuSpeed.name()); - if ((cpuSpeed == null) || (NumbersUtil.parseInt(cpuSpeed, -1) <= 0)) { - throw new InvalidParameterValueException("Invalid CPU speed value, specify a value between 1 and " + Integer.MAX_VALUE); - } - } else if (!serviceOffering.isCustomCpuSpeedSupported() && customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) { - throw new InvalidParameterValueException("The CPU speed of this offering id:" + serviceOffering.getUuid() - + " is not customizable. This is predefined in the Template."); - } - - if (serviceOffering.getRamSize() == null) { - int minMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_MEMORY), 32); - int maxMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_MEMORY), Integer.MAX_VALUE); - int memory = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.memory.name()), -1); - Integer maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value(); - if (memory < minMemory || memory > maxMemory || memory > maxRAMSize) { - throw new InvalidParameterValueException(String.format("Invalid memory value, specify a value between %d and %d", minMemory, Math.min(maxRAMSize, maxMemory))); - } - } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.memory.name())) { - throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getUuid() + " is not customizable. This is predefined in the Template."); - } - } else { + if (MapUtils.isEmpty(customParameters) && serviceOffering.isDynamic()) { throw new InvalidParameterValueException("Need to specify custom parameter values cpu, cpu speed and memory when using custom offering"); } + Map offeringDetails = serviceOfferingDetailsDao.listDetailsKeyPairs(serviceOffering.getId()); + if (serviceOffering.getCpu() == null) { + int minCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_CPU_NUMBER), 1); + int maxCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_CPU_NUMBER), Integer.MAX_VALUE); + int cpuNumber = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name()), -1); + int maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value(); + if (cpuNumber < minCPU || cpuNumber > maxCPU || cpuNumber > maxCPUCores) { + throw new InvalidParameterValueException(String.format("Invalid CPU cores value, specify a value between %d and %d", minCPU, Math.min(maxCPUCores, maxCPU))); + } + } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) { + throw new InvalidParameterValueException("The CPU cores of this offering id:" + serviceOffering.getUuid() + + " is not customizable. This is predefined in the Template."); + } + + if (serviceOffering.getSpeed() == null) { + String cpuSpeed = customParameters.get(UsageEventVO.DynamicParameters.cpuSpeed.name()); + if ((cpuSpeed == null) || (NumbersUtil.parseInt(cpuSpeed, -1) <= 0)) { + throw new InvalidParameterValueException("Invalid CPU speed value, specify a value between 1 and " + Integer.MAX_VALUE); + } + } else if (!serviceOffering.isCustomCpuSpeedSupported() && customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) { + throw new InvalidParameterValueException(String.format("The CPU speed of this offering id:%s" + + " is not customizable. This is predefined as %d MHz.", + serviceOffering.getUuid(), serviceOffering.getSpeed())); + } + + if (serviceOffering.getRamSize() == null) { + int minMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_MEMORY), 32); + int maxMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_MEMORY), Integer.MAX_VALUE); + int memory = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.memory.name()), -1); + int maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value(); + if (memory < minMemory || memory > maxMemory || memory > maxRAMSize) { + throw new InvalidParameterValueException(String.format("Invalid memory value, specify a value between %d and %d", minMemory, Math.min(maxRAMSize, maxMemory))); + } + } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.memory.name())) { + throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getUuid() + " is not customizable. This is predefined in the Template."); + } } private UserVm upgradeStoppedVirtualMachine(Long vmId, Long svcOffId, Map customParameters) throws ResourceAllocationException { @@ -1396,30 +1395,30 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List reservations = new ArrayList<>(); try { - if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, - (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations); - } + if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, + (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations); + } - // Check that the specified service offering ID is valid - _itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering); + // Check that the specified service offering ID is valid + _itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering); - // Check if the new service offering can be applied to vm instance - _accountMgr.checkAccess(owner, newServiceOffering, _dcDao.findById(vmInstance.getDataCenterId())); + // Check if the new service offering can be applied to vm instance + _accountMgr.checkAccess(owner, newServiceOffering, _dcDao.findById(vmInstance.getDataCenterId())); - // resize and migrate the root volume if required - DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newServiceOffering.getDiskOfferingId()); - changeDiskOfferingForRootVolume(vmId, newDiskOffering, customParameters, vmInstance.getDataCenterId()); + // resize and migrate the root volume if required + DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newServiceOffering.getDiskOfferingId()); + changeDiskOfferingForRootVolume(vmId, newDiskOffering, customParameters, vmInstance.getDataCenterId()); - _itMgr.upgradeVmDb(vmId, newServiceOffering, currentServiceOffering); + _itMgr.upgradeVmDb(vmId, newServiceOffering, currentServiceOffering); - // Increment or decrement CPU and Memory count accordingly. - if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - _resourceLimitMgr.updateVmResourceCountForServiceOfferingChange(owner.getAccountId(), vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, - (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template); - } + // Increment or decrement CPU and Memory count accordingly. + if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + _resourceLimitMgr.updateVmResourceCountForServiceOfferingChange(owner.getAccountId(), vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, + (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template); + } - return _vmDao.findById(vmInstance.getId()); + return _vmDao.findById(vmInstance.getId()); } finally { ReservationHelper.closeAll(reservations); @@ -2384,34 +2383,34 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List reservations = new ArrayList<>(); try { - // First check that the maximum number of UserVMs, CPU and Memory limit for the given - // accountId will not be exceeded - if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template, reservations); - } + // First check that the maximum number of UserVMs, CPU and Memory limit for the given + // accountId will not be exceeded + if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template, reservations); + } - _haMgr.cancelDestroy(vm, vm.getHostId()); + _haMgr.cancelDestroy(vm, vm.getHostId()); - try { - if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.RecoveryRequested, null)) { - logger.debug("Unable to recover the vm {} because it is not in the correct state. current state: {}", vm, vm.getState()); + try { + if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.RecoveryRequested, null)) { + logger.debug("Unable to recover the vm {} because it is not in the correct state. current state: {}", vm, vm.getState()); + throw new InvalidParameterValueException(String.format("Unable to recover the vm %s because it is not in the correct state. current state: %s", vm, vm.getState())); + } + } catch (NoTransitionException e) { throw new InvalidParameterValueException(String.format("Unable to recover the vm %s because it is not in the correct state. current state: %s", vm, vm.getState())); } - } catch (NoTransitionException e) { - throw new InvalidParameterValueException(String.format("Unable to recover the vm %s because it is not in the correct state. current state: %s", vm, vm.getState())); - } - // Recover the VM's disks - List volumes = _volsDao.findByInstance(vmId); - for (VolumeVO volume : volumes) { - if (volume.getVolumeType().equals(Volume.Type.ROOT)) { - recoverRootVolume(volume, vmId); - break; + // Recover the VM's disks + List volumes = _volsDao.findByInstance(vmId); + for (VolumeVO volume : volumes) { + if (volume.getVolumeType().equals(Volume.Type.ROOT)) { + recoverRootVolume(volume, vmId); + break; + } } - } - //Update Resource Count for the given account - resourceCountIncrement(account.getId(), vm.isDisplayVm(), serviceOffering, template); + //Update Resource Count for the given account + resourceCountIncrement(account.getId(), vm.isDisplayVm(), serviceOffering, template); } finally { ReservationHelper.closeAll(reservations); @@ -2839,10 +2838,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Map customParameters = new HashMap<>(); customParameters.put(VmDetailConstants.CPU_NUMBER, String.valueOf(newCpu)); customParameters.put(VmDetailConstants.MEMORY, String.valueOf(newMemory)); - if (svcOffering.isCustomCpuSpeedSupported()) { + if (details.containsKey(VmDetailConstants.CPU_SPEED)) { customParameters.put(VmDetailConstants.CPU_SPEED, details.get(VmDetailConstants.CPU_SPEED)); } validateCustomParameters(svcOffering, customParameters); + } else { + if (details.containsKey(VmDetailConstants.CPU_NUMBER) || details.containsKey(VmDetailConstants.MEMORY) || + details.containsKey(VmDetailConstants.CPU_SPEED)) { + throw new InvalidParameterValueException("CPU number, Memory and CPU speed cannot be updated for a " + + "non-dynamic offering"); + } } if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) { return; diff --git a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java index a703b737282..bfec3247a7b 100644 --- a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java @@ -757,11 +757,17 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme "Instance Snapshot reverting failed because the Instance is not in Running or Stopped state."); } - if (userVm.getState() == VirtualMachine.State.Running && vmSnapshotVo.getType() == VMSnapshot.Type.Disk || userVm.getState() == VirtualMachine.State.Stopped - && vmSnapshotVo.getType() == VMSnapshot.Type.DiskAndMemory) { + if (userVm.getState() == VirtualMachine.State.Running && vmSnapshotVo.getType() == VMSnapshot.Type.Disk) { throw new InvalidParameterValueException( - "Reverting to the Instance Snapshot is not allowed for running Instances as this would result in a Instance state change. For running Instances only Snapshots with memory can be reverted. In order to revert to a Snapshot without memory you need to first stop the Instance." - + " Snapshot"); + "Reverting to the Instance Snapshot is not allowed for running Instances as this would result in an Instance state change. " + + "For running Instances only Snapshots with memory can be reverted. " + + "In order to revert to a Snapshot without memory you need to first stop the Instance."); + } + + if (userVm.getState() == VirtualMachine.State.Stopped && vmSnapshotVo.getType() == VMSnapshot.Type.DiskAndMemory) { + throw new InvalidParameterValueException( + "Reverting to the Instance Snapshot is not allowed for stopped Instances when the Snapshot contains memory as this would result in an Instance state change. " + + "In order to revert to a Snapshot with memory you need to first start the Instance."); } // if snapshot is not created, error out @@ -814,20 +820,36 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme } /** - * If snapshot was taken with a different service offering than actual used in vm, should change it back to it. - * We also call changeUserVmServiceOffering in case the service offering is dynamic in order to - * perform resource limit validation, as the amount of CPUs or memory may have been changed. - * @param vmSnapshotVo vm snapshot + * Check if service offering change is needed for user vm when reverting to vm snapshot. + * Service offering change is needed when snapshot was taken with a different service offering than actual used in vm. + * Service offering change is also needed when service offering is dynamic and the amount of cpu, memory or cpu speed + * has been changed since snapshot was taken. + * @param userVm + * @param vmSnapshotVo + * @return true if service offering change is needed; false otherwise */ - protected void updateUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) { + protected boolean userVmServiceOfferingNeedsChange(UserVm userVm, VMSnapshotVO vmSnapshotVo) { if (vmSnapshotVo.getServiceOfferingId() != userVm.getServiceOfferingId()) { - changeUserVmServiceOffering(userVm, vmSnapshotVo); - return; + return true; } - ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(userVm.getServiceOfferingId()); - if (serviceOffering.isDynamic()) { - changeUserVmServiceOffering(userVm, vmSnapshotVo); + + ServiceOfferingVO currentServiceOffering = _serviceOfferingDao.findByIdIncludingRemoved(userVm.getId(), userVm.getServiceOfferingId()); + if (currentServiceOffering.isDynamic()) { + Map vmDetails = getVmMapDetails(vmSnapshotVo); + ServiceOfferingVO newServiceOffering = _serviceOfferingDao.getComputeOffering(currentServiceOffering, vmDetails); + + int newCpu = newServiceOffering.getCpu(); + int newMemory = newServiceOffering.getRamSize(); + int newSpeed = newServiceOffering.getSpeed(); + int currentCpu = currentServiceOffering.getCpu(); + int currentMemory = currentServiceOffering.getRamSize(); + int currentSpeed = currentServiceOffering.getSpeed(); + + if (newCpu != currentCpu || newMemory != currentMemory || newSpeed != currentSpeed) { + return true; + } } + return false; } /** @@ -947,8 +969,10 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) throws CloudRuntimeException { + if (userVmServiceOfferingNeedsChange(userVm, vmSnapshotVo)) { + changeUserVmServiceOffering(userVm, vmSnapshotVo); + } revertCustomServiceOfferingDetailsFromVmSnapshot(userVm, vmSnapshotVo); - updateUserVmServiceOffering(userVm, vmSnapshotVo); } }); return userVm; diff --git a/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java b/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java index 37e4f35a9af..abc2e5ca225 100644 --- a/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java @@ -212,20 +212,20 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ List reservations = new ArrayList<>(); try { - // 6. check resource limitation - checkResourceLimitForImportVolume(owner, volume, diskOffering, reservations); + // 6. check resource limitation + checkResourceLimitForImportVolume(owner, volume, diskOffering, reservations); - // 7. create records - String volumeName = StringUtils.isNotBlank(cmd.getName()) ? cmd.getName().trim() : volumePath; - VolumeVO volumeVO = importVolumeInternal(volume, diskOffering, owner, pool, volumeName); + // 7. create records + String volumeName = StringUtils.isNotBlank(cmd.getName()) ? cmd.getName().trim() : volumePath; + VolumeVO volumeVO = importVolumeInternal(volume, diskOffering, owner, pool, volumeName); - // 8. Update resource count - updateResourceLimitForVolumeImport(volumeVO); + // 8. Update resource count + updateResourceLimitForVolumeImport(volumeVO); - // 9. Publish event - publicUsageEventForVolumeImportAndUnmanage(volumeVO, true); + // 9. Publish event + publicUsageEventForVolumeImportAndUnmanage(volumeVO, true); - return responseGenerator.createVolumeResponse(ResponseObject.ResponseView.Full, volumeVO); + return responseGenerator.createVolumeResponse(ResponseObject.ResponseView.Full, volumeVO); } finally { ReservationHelper.closeAll(reservations); diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 20bd463c049..56998325ba7 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1500,7 +1500,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { String hostName, Account caller, Account owner, long userId, ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap, Map nicNetworkMap, Map nicIpAddressMap, - Map details, Boolean migrateAllowed, List managedVms, boolean forced) { + Map details, Boolean migrateAllowed, List managedVms, boolean forced) throws ResourceAllocationException { UserVm userVm = null; for (HostVO host : hosts) { HashMap unmanagedInstances = getUnmanagedInstancesForHost(host, instanceName, managedVms); @@ -1544,11 +1544,18 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { template.setGuestOSId(guestOSHypervisor.getGuestOsId()); } - userVm = importVirtualMachineInternal(unmanagedInstance, instanceName, zone, cluster, host, - template, displayName, hostName, CallContext.current().getCallingAccount(), owner, userId, - serviceOffering, dataDiskOfferingMap, - nicNetworkMap, nicIpAddressMap, null, - details, migrateAllowed, forced, true); + + List reservations = new ArrayList<>(); + try { + checkVmResourceLimitsForUnmanagedInstanceImport(owner, unmanagedInstance, serviceOffering, template, reservations); + userVm = importVirtualMachineInternal(unmanagedInstance, instanceName, zone, cluster, host, + template, displayName, hostName, CallContext.current().getCallingAccount(), owner, userId, + serviceOffering, dataDiskOfferingMap, + nicNetworkMap, nicIpAddressMap, null, + details, migrateAllowed, forced, true); + } finally { + ReservationHelper.closeAll(reservations); + } break; } if (userVm != null) { @@ -1558,6 +1565,36 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { return userVm; } + protected void checkVmResourceLimitsForUnmanagedInstanceImport(Account owner, UnmanagedInstanceTO unmanagedInstance, ServiceOfferingVO serviceOffering, VMTemplateVO template, List reservations) throws ResourceAllocationException { + // When importing an unmanaged instance, the amount of CPUs and memory is obtained from the hypervisor unless powered off + // and not using a dynamic offering, unlike the external VM import that always obtains it from the compute offering + Integer cpu = serviceOffering.getCpu(); + Integer memory = serviceOffering.getRamSize(); + + if (serviceOffering.isDynamic() || !UnmanagedInstanceTO.PowerState.PowerOff.equals(unmanagedInstance.getPowerState())) { + cpu = unmanagedInstance.getCpuCores(); + memory = unmanagedInstance.getMemory(); + } + + if (cpu == null || cpu == 0) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("CPU cores [%s] is not valid for importing VM [%s].", cpu, unmanagedInstance.getName())); + } + if (memory == null || memory == 0) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Memory [%s] is not valid for importing VM [%s].", memory, unmanagedInstance.getName())); + } + + List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); + + CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); + reservations.add(vmReservation); + + CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, cpu.longValue(), reservationDao, resourceLimitService); + reservations.add(cpuReservation); + + CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, memory.longValue(), reservationDao, resourceLimitService); + reservations.add(memReservation); + } + private Pair getSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username, String password, String clusterName, String sourceHostName, String sourceVM, ServiceOfferingVO serviceOffering) { @@ -1614,7 +1651,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { Account caller, Account owner, long userId, ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap, Map nicNetworkMap, Map nicIpAddressMap, - Map details, ImportVmCmd cmd, boolean forced) { + Map details, ImportVmCmd cmd, boolean forced) throws ResourceAllocationException { Long existingVcenterId = cmd.getExistingVcenterId(); String vcenter = cmd.getVcenter(); String datacenterName = cmd.getDatacenterName(); @@ -1668,6 +1705,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { DataStoreTO temporaryConvertLocation = null; String ovfTemplateOnConvertLocation = null; ImportVmTask importVMTask = null; + List reservations = new ArrayList<>(); try { HostVO convertHost = selectKVMHostForConversionInCluster(destinationCluster, convertInstanceHostId, useVddk); HostVO importHost = (useVddk && importInstanceHostId == null) @@ -1697,6 +1735,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { Pair sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName, serviceOffering); sourceVMwareInstance = sourceInstanceDetails.first(); isClonedInstance = sourceInstanceDetails.second(); + + // Ensure that the configured resource limits will not be exceeded before beginning the conversion process + checkVmResourceLimitsForUnmanagedInstanceImport(owner, sourceVMwareInstance, serviceOffering, template, reservations); + boolean isWindowsVm = sourceVMwareInstance.getOperatingSystem().toLowerCase().contains("windows"); if (isWindowsVm) { checkConversionSupportOnHost(convertHost, sourceVMName, true, useVddk, details); @@ -1749,6 +1791,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (temporaryConvertLocation != null && StringUtils.isNotBlank(ovfTemplateOnConvertLocation)) { removeTemplate(temporaryConvertLocation, ovfTemplateOnConvertLocation); } + ReservationHelper.closeAll(reservations); } } @@ -2696,6 +2739,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List reservations = new ArrayList<>(); try { + checkVmResourceLimitsForExternalKvmVmImport(owner, serviceOffering, (VMTemplateVO) template, details, reservations); checkVolumeResourceLimitsForExternalKvmVmImport(owner, rootDisk, dataDisks, diskOffering, dataDiskOfferingMap, reservations); // Check NICs and supplied networks @@ -2860,101 +2904,138 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { profiles.add(nicProfile); networkNicMap.put(network.getUuid(), profiles); + List reservations = new ArrayList<>(); try { + checkVmResourceLimitsForExternalKvmVmImport(owner, serviceOffering, (VMTemplateVO) template, details, reservations); userVm = userVmManager.importVM(zone, null, template, null, displayName, owner, null, caller, true, null, owner.getAccountId(), userId, serviceOffering, null, null, hostName, Hypervisor.HypervisorType.KVM, allDetails, powerState, networkNicMap); - } catch (InsufficientCapacityException ice) { + + if (userVm == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); + } + + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); + List resourceLimitStorageTags = resourceLimitService.getResourceLimitStorageTagsForResourceCountOperation(true, diskOffering); + CheckedReservation volumeReservation = new CheckedReservation(owner, Resource.ResourceType.volume, resourceLimitStorageTags, + CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? 1L : 0L, reservationDao, resourceLimitService); + reservations.add(volumeReservation); + + String rootVolumeName = String.format("ROOT-%s", userVm.getId()); + DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false); + + final VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm, template, serviceOffering, owner, null); + ServiceOfferingVO dummyOffering = serviceOfferingDao.findById(userVm.getId(), serviceOffering.getId()); + profile.setServiceOffering(dummyOffering); + DeploymentPlanner.ExcludeList excludeList = new DeploymentPlanner.ExcludeList(); + final DataCenterDeployment plan = new DataCenterDeployment(zone.getId(), null, null, hostId, poolId, null); + DeployDestination dest = null; + try { + dest = deploymentPlanningManager.planDeployment(profile, plan, excludeList, null); + } catch (Exception e) { + logger.warn("Import failed for Vm: {} while finding deployment destination", userVm, e); + cleanupFailedImportVM(userVm); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s while finding deployment destination", userVm.getInstanceName())); + } + if(dest == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName())); + } + + Map storage = dest.getStorageForDisks(); + Volume volume = volumeDao.findById(diskProfile.getVolumeId()); + StoragePool storagePool = storage.get(volume); + CheckVolumeCommand checkVolumeCommand = new CheckVolumeCommand(); + checkVolumeCommand.setSrcFile(diskPath); + StorageFilerTO storageTO = new StorageFilerTO(storagePool); + checkVolumeCommand.setStorageFilerTO(storageTO); + Answer answer = agentManager.easySend(dest.getHost().getId(), checkVolumeCommand); + if (!(answer instanceof CheckVolumeAnswer)) { + cleanupFailedImportVM(userVm); + throw new CloudRuntimeException("Disk not found or is invalid"); + } + CheckVolumeAnswer checkVolumeAnswer = (CheckVolumeAnswer) answer; + try { + checkVolume(checkVolumeAnswer.getVolumeDetails()); + } catch (CloudRuntimeException e) { + cleanupFailedImportVM(userVm); + throw e; + } + if (!checkVolumeAnswer.getResult()) { + cleanupFailedImportVM(userVm); + throw new CloudRuntimeException("Disk not found or is invalid"); + } + diskProfile.setSize(checkVolumeAnswer.getSize()); + + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.primary_storage, resourceLimitStorageTags, + CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? diskProfile.getSize() : 0L, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); + + List> diskProfileStoragePoolList = new ArrayList<>(); + try { + long deviceId = 1L; + if(ImportSource.SHARED == importSource) { + diskProfileStoragePoolList.add(importKVMSharedDisk(userVm, diskOffering, Volume.Type.ROOT, + template, deviceId, poolId, diskPath, diskProfile)); + } else if(ImportSource.LOCAL == importSource) { + diskProfileStoragePoolList.add(importKVMLocalDisk(userVm, diskOffering, Volume.Type.ROOT, + template, deviceId, hostId, diskPath, diskProfile)); + } + } catch (Exception e) { + logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e); + cleanupFailedImportVM(userVm); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); + } + networkOrchestrationService.importNic(macAddress, 0, network, true, userVm, requestedIpPair, zone, true); + publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); + return userVm; + + } catch (InsufficientCapacityException ice) { // This will be thrown by com.cloud.vm.UserVmService.importVM logger.error(String.format("Failed to import vm name: %s", instanceName), ice); throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage()); - } - if (userVm == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); - } - - DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); - - List reservations = new ArrayList<>(); - List resourceLimitStorageTags = resourceLimitService.getResourceLimitStorageTagsForResourceCountOperation(true, diskOffering); - try { - CheckedReservation volumeReservation = new CheckedReservation(owner, Resource.ResourceType.volume, resourceLimitStorageTags, - CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? 1L : 0L, reservationDao, resourceLimitService); - reservations.add(volumeReservation); - - String rootVolumeName = String.format("ROOT-%s", userVm.getId()); - DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false); - - final VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm, template, serviceOffering, owner, null); - ServiceOfferingVO dummyOffering = serviceOfferingDao.findById(userVm.getId(), serviceOffering.getId()); - profile.setServiceOffering(dummyOffering); - DeploymentPlanner.ExcludeList excludeList = new DeploymentPlanner.ExcludeList(); - final DataCenterDeployment plan = new DataCenterDeployment(zone.getId(), null, null, hostId, poolId, null); - DeployDestination dest = null; - try { - dest = deploymentPlanningManager.planDeployment(profile, plan, excludeList, null); - } catch (Exception e) { - logger.warn("Import failed for Vm: {} while finding deployment destination", userVm, e); - cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s while finding deployment destination", userVm.getInstanceName())); - } - if(dest == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName())); - } - - Map storage = dest.getStorageForDisks(); - Volume volume = volumeDao.findById(diskProfile.getVolumeId()); - StoragePool storagePool = storage.get(volume); - CheckVolumeCommand checkVolumeCommand = new CheckVolumeCommand(); - checkVolumeCommand.setSrcFile(diskPath); - StorageFilerTO storageTO = new StorageFilerTO(storagePool); - checkVolumeCommand.setStorageFilerTO(storageTO); - Answer answer = agentManager.easySend(dest.getHost().getId(), checkVolumeCommand); - if (!(answer instanceof CheckVolumeAnswer)) { - cleanupFailedImportVM(userVm); - throw new CloudRuntimeException("Disk not found or is invalid"); - } - CheckVolumeAnswer checkVolumeAnswer = (CheckVolumeAnswer) answer; - try { - checkVolume(checkVolumeAnswer.getVolumeDetails()); - } catch (CloudRuntimeException e) { + } catch (ResourceAllocationException e) { cleanupFailedImportVM(userVm); throw e; - } - if (!checkVolumeAnswer.getResult()) { - cleanupFailedImportVM(userVm); - throw new CloudRuntimeException("Disk not found or is invalid"); - } - diskProfile.setSize(checkVolumeAnswer.getSize()); - - CheckedReservation primaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.primary_storage, resourceLimitStorageTags, - CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? diskProfile.getSize() : 0L, reservationDao, resourceLimitService); - reservations.add(primaryStorageReservation); - - List> diskProfileStoragePoolList = new ArrayList<>(); - try { - long deviceId = 1L; - if(ImportSource.SHARED == importSource) { - diskProfileStoragePoolList.add(importKVMSharedDisk(userVm, diskOffering, Volume.Type.ROOT, - template, deviceId, poolId, diskPath, diskProfile)); - } else if(ImportSource.LOCAL == importSource) { - diskProfileStoragePoolList.add(importKVMLocalDisk(userVm, diskOffering, Volume.Type.ROOT, - template, deviceId, hostId, diskPath, diskProfile)); - } - } catch (Exception e) { - logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e); - cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); - } - networkOrchestrationService.importNic(macAddress, 0, network, true, userVm, requestedIpPair, zone, true); - publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); - return userVm; - } finally { ReservationHelper.closeAll(reservations); } } + protected void checkVmResourceLimitsForExternalKvmVmImport(Account owner, ServiceOfferingVO serviceOffering, VMTemplateVO template, Map details, List reservations) throws ResourceAllocationException { + // When importing an external VM, the amount of CPUs and memory is always obtained from the compute offering, + // unlike the unmanaged instance import that obtains it from the hypervisor unless the VM is powered off and the offering is fixed + Integer cpu = serviceOffering.getCpu(); + Integer memory = serviceOffering.getRamSize(); + + if (serviceOffering.isDynamic()) { + cpu = getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details); + memory = getDetailAsInteger(VmDetailConstants.MEMORY, details); + } + + List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); + + CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); + reservations.add(vmReservation); + + CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, cpu.longValue(), reservationDao, resourceLimitService); + reservations.add(cpuReservation); + + CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, memory.longValue(), reservationDao, resourceLimitService); + reservations.add(memReservation); + } + + protected Integer getDetailAsInteger(String key, Map details) { + String detail = details.get(key); + if (detail == null) { + throw new InvalidParameterValueException(String.format("Detail '%s' must be provided.", key)); + } + try { + return Integer.valueOf(detail); + } catch (NumberFormatException e) { + throw new InvalidParameterValueException(String.format("Please provide a valid integer value for detail '%s'.", key)); + } + } + private void checkVolume(Map volumeDetails) { if (MapUtils.isEmpty(volumeDetails)) { return; diff --git a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java index d8d62df1a72..d94f9db0c99 100644 --- a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java +++ b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java @@ -16,6 +16,25 @@ // under the License. package com.cloud.hypervisor; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.configuration.ConfigurationManagerImpl; @@ -34,23 +53,6 @@ import com.cloud.storage.dao.GuestOSHypervisorDao; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; - -import java.io.UnsupportedEncodingException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; @RunWith(MockitoJUnitRunner.class) public class KVMGuruTest { @@ -111,8 +113,15 @@ public class KVMGuruTest { private static final String detail2Key = "detail2"; private static final String detail2Value = "value2"; + private ConfigKey originalVmServiceOfferingMaxCpuCores; + private ConfigKey originalVmServiceOfferingMaxRAMSize; + @Before public void setup() throws UnsupportedEncodingException { + // Preserve the original value for restoration in tearDown + originalVmServiceOfferingMaxCpuCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES; + originalVmServiceOfferingMaxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE; + Mockito.when(vmTO.isLimitCpuUse()).thenReturn(true); Mockito.when(vmProfile.getVirtualMachine()).thenReturn(vm); Mockito.when(vm.getHostId()).thenReturn(hostId); @@ -134,6 +143,13 @@ public class KVMGuruTest { Arrays.asList(detail1, detail2)); } + @After + public void tearDown() { + // Restore the original value + ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES = originalVmServiceOfferingMaxCpuCores; + ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE = originalVmServiceOfferingMaxRAMSize; + } + @Test public void testSetVmQuotaPercentage() { guru.setVmQuotaPercentage(vmTO, vmProfile); diff --git a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java index 3f7ee3e0bc0..a472a883d87 100644 --- a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java @@ -554,6 +554,7 @@ public class VpcManagerImplTest { doReturn(ipv4GuestSubnetNetworkMap).when(routedIpv4Manager).getOrCreateIpv4SubnetForVpc(any(), anyInt()); List bgpPeerIds = Arrays.asList(11L, 12L); try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, null, vpcDomain, ip4Dns[0], ip4Dns[1], null, null, true, 1500, 24, null, bgpPeerIds, false); } catch (ResourceAllocationException e) { diff --git a/server/src/test/java/com/cloud/template/TemplateManagerImplTest.java b/server/src/test/java/com/cloud/template/TemplateManagerImplTest.java index 4f86f578a69..6288180a9f4 100755 --- a/server/src/test/java/com/cloud/template/TemplateManagerImplTest.java +++ b/server/src/test/java/com/cloud/template/TemplateManagerImplTest.java @@ -107,8 +107,8 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.heuristics.HeuristicRuleHelper; import org.apache.cloudstack.storage.template.VnfTemplateManager; - import org.apache.cloudstack.test.utils.SpringUtils; + import org.junit.After; import org.junit.Assert; import org.junit.Before; diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index aa35eb8472f..cb15298fbf3 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -59,20 +59,12 @@ import java.util.Map; import java.util.TimeZone; import java.util.UUID; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockedConstruction; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.test.util.ReflectionTestUtils; - +import com.cloud.network.as.AutoScaleManager; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.LoadBalancerVMMapDao; +import com.cloud.network.dao.LoadBalancerVMMapVO; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -107,6 +99,19 @@ import org.apache.cloudstack.storage.template.VnfTemplateManager; import org.apache.cloudstack.userdata.UserDataManager; import org.apache.cloudstack.vm.UnmanagedVMsManager; import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; import com.cloud.api.query.dao.ServiceOfferingJoinDao; import com.cloud.api.query.vo.ServiceOfferingJoinVO; @@ -136,12 +141,6 @@ import com.cloud.hypervisor.Hypervisor; import com.cloud.kubernetes.cluster.KubernetesServiceHelper; import com.cloud.network.Network; import com.cloud.network.NetworkModel; -import com.cloud.network.as.AutoScaleManager; -import com.cloud.network.dao.FirewallRulesDao; -import com.cloud.network.dao.IPAddressDao; -import com.cloud.network.dao.IPAddressVO; -import com.cloud.network.dao.LoadBalancerVMMapDao; -import com.cloud.network.dao.LoadBalancerVMMapVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.element.UserDataServiceProvider; @@ -162,6 +161,7 @@ import com.cloud.resourcelimit.CheckedReservation; import com.cloud.server.ManagementService; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.ScopeType; @@ -452,6 +452,9 @@ public class UserVmManagerImplTest { MockedStatic unmanagedVMsManagerMockedStatic; + @Mock + ServiceOfferingDetailsDao serviceOfferingDetailsDao; + private static final long vmId = 1l; private static final long zoneId = 2L; private static final long accountId = 3L; @@ -4224,4 +4227,96 @@ public class UserVmManagerImplTest { method.setAccessible(true); method.invoke(userVmManagerImpl, vmId); } + + private ServiceOfferingVO getMockedServiceOffering(boolean custom, boolean customSpeed) { + ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class); + when(serviceOffering.getUuid()).thenReturn("offering-uuid"); + when(serviceOffering.isDynamic()).thenReturn(custom); + when(serviceOffering.isCustomCpuSpeedSupported()).thenReturn(customSpeed); + if (custom) { + when(serviceOffering.getCpu()).thenReturn(null); + when(serviceOffering.getRamSize()).thenReturn(null); + } + if (customSpeed) { + when(serviceOffering.getSpeed()).thenReturn(null); + } else { + when(serviceOffering.isCustomCpuSpeedSupported()).thenReturn(false); + when(serviceOffering.getSpeed()).thenReturn(1000); + } + return serviceOffering; + } + + @Test + public void customOfferingNeedsCustomizationThrowsException() { + ServiceOfferingVO serviceOffering = getMockedServiceOffering(true, true); + InvalidParameterValueException ex = Assert.assertThrows(InvalidParameterValueException.class, () -> + userVmManagerImpl.validateCustomParameters(serviceOffering, Collections.emptyMap())); + assertEquals("Need to specify custom parameter values cpu, cpu speed and memory when using custom offering", ex.getMessage()); + } + + @Test + public void cpuSpeedCustomizationNotAllowedThrowsException() { + ServiceOfferingVO serviceOffering = getMockedServiceOffering(true, false); + + Map customParameters = new HashMap<>(); + customParameters.put(VmDetailConstants.CPU_NUMBER, "1"); + customParameters.put(VmDetailConstants.CPU_SPEED, "2500"); + + InvalidParameterValueException ex = Assert.assertThrows(InvalidParameterValueException.class, () -> + userVmManagerImpl.validateCustomParameters(serviceOffering, customParameters)); + Assert.assertTrue(ex.getMessage().startsWith("The CPU speed of this offering")); + } + + @Test + public void cpuSpeedCustomizationAllowedDoesNotThrowException() { + ServiceOfferingVO serviceOffering = getMockedServiceOffering(true, true); + + when(serviceOfferingDetailsDao.listDetailsKeyPairs(anyLong())).thenReturn( + Map.of(ApiConstants.MIN_CPU_NUMBER, "1", + ApiConstants.MAX_CPU_NUMBER, "4", + ApiConstants.MIN_MEMORY, "256", + ApiConstants.MAX_MEMORY, "8192")); + + Map customParameters = new HashMap<>(); + customParameters.put(VmDetailConstants.CPU_NUMBER, "1"); + customParameters.put(VmDetailConstants.CPU_SPEED, "2500"); + customParameters.put(VmDetailConstants.MEMORY, "256"); + + userVmManagerImpl.validateCustomParameters(serviceOffering, customParameters); + } + + @Test + public void verifyVmLimits_fixedOffering_throwsException() { + when(userVmVoMock.getId()).thenReturn(1L); + when(userVmVoMock.getServiceOfferingId()).thenReturn(1L); + when(accountDao.findById(anyLong())).thenReturn(callerAccount); + ServiceOfferingVO serviceOffering = getMockedServiceOffering(false, false); + when(_serviceOfferingDao.findById(anyLong())).thenReturn(serviceOffering); + when(_serviceOfferingDao.findByIdIncludingRemoved(anyLong(), anyLong())).thenReturn(serviceOffering); + + Map customParameters = new HashMap<>(); + customParameters.put(VmDetailConstants.CPU_SPEED, "2500"); + + InvalidParameterValueException ex = Assert.assertThrows(InvalidParameterValueException.class, () -> + userVmManagerImpl.verifyVmLimits(userVmVoMock, customParameters)); + assertEquals("CPU number, Memory and CPU speed cannot be updated for a non-dynamic offering", ex.getMessage()); + } + + @Test + public void verifyVmLimits_constrainedOffering_throwsException() { + when(userVmVoMock.getId()).thenReturn(1L); + when(userVmVoMock.getServiceOfferingId()).thenReturn(1L); + when(accountDao.findById(anyLong())).thenReturn(callerAccount); + ServiceOfferingVO serviceOffering = getMockedServiceOffering(true, false); + when(_serviceOfferingDao.findById(anyLong())).thenReturn(serviceOffering); + when(_serviceOfferingDao.findByIdIncludingRemoved(anyLong(), anyLong())).thenReturn(serviceOffering); + + Map customParameters = new HashMap<>(); + customParameters.put(VmDetailConstants.CPU_NUMBER, "1"); + customParameters.put(VmDetailConstants.CPU_SPEED, "2500"); + + InvalidParameterValueException ex = Assert.assertThrows(InvalidParameterValueException.class, () -> + userVmManagerImpl.verifyVmLimits(userVmVoMock, customParameters)); + Assert.assertTrue(ex.getMessage().startsWith("The CPU speed of this offering")); + } } diff --git a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java index 2a09bb81ea8..9a0007c9a83 100644 --- a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java +++ b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java @@ -228,6 +228,7 @@ public class VMSnapshotManagerTest { when(vmSnapshotVO.getId()).thenReturn(VM_SNAPSHOT_ID); when(serviceOffering.isDynamic()).thenReturn(false); when(_serviceOfferingDao.findById(SERVICE_OFFERING_ID)).thenReturn(serviceOffering); + when(_serviceOfferingDao.findByIdIncludingRemoved(TEST_VM_ID, SERVICE_OFFERING_ID)).thenReturn(serviceOffering); for (ResourceDetail detail : Arrays.asList(userVmDetailCpuNumber, vmSnapshotDetailCpuNumber)) { when(detail.getName()).thenReturn(VmDetailConstants.CPU_NUMBER); @@ -345,20 +346,51 @@ public class VMSnapshotManagerTest { } @Test - public void testUpdateUserVmServiceOfferingSameServiceOffering() { - _vmSnapshotMgr.updateUserVmServiceOffering(userVm, vmSnapshotVO); - verify(_vmSnapshotMgr, never()).changeUserVmServiceOffering(userVm, vmSnapshotVO); + public void testUserVmServiceOfferingNeedsChangeWhenSnapshotOfferingDiffers() { + when(userVm.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_DIFFERENT_ID); + when(vmSnapshotVO.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_ID); + + assertTrue(_vmSnapshotMgr.userVmServiceOfferingNeedsChange(userVm, vmSnapshotVO)); + + verify(_serviceOfferingDao, never()).findByIdIncludingRemoved(anyLong(), anyLong()); + verify(_serviceOfferingDao, never()).getComputeOffering(any(ServiceOfferingVO.class), any()); } @Test - public void testUpdateUserVmServiceOfferingDifferentServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException { - when(userVm.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_DIFFERENT_ID); - when(_userVmManager.upgradeVirtualMachine(eq(TEST_VM_ID), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true); - _vmSnapshotMgr.updateUserVmServiceOffering(userVm, vmSnapshotVO); + public void testUserVmServiceOfferingNeedsChangeWhenSameNonDynamicOffering() { + assertFalse(_vmSnapshotMgr.userVmServiceOfferingNeedsChange(userVm, vmSnapshotVO)); - verify(_vmSnapshotMgr).changeUserVmServiceOffering(userVm, vmSnapshotVO); + verify(_serviceOfferingDao).findByIdIncludingRemoved(TEST_VM_ID, SERVICE_OFFERING_ID); + verify(_serviceOfferingDao, never()).getComputeOffering(any(ServiceOfferingVO.class), any()); + } + + @Test + public void testUserVmServiceOfferingNeedsChangeWhenDynamicOfferingMatchesSnapshot() { + when(serviceOffering.isDynamic()).thenReturn(true); + when(serviceOffering.getCpu()).thenReturn(2); + when(serviceOffering.getRamSize()).thenReturn(2048); + when(serviceOffering.getSpeed()).thenReturn(1000); + when(_serviceOfferingDao.getComputeOffering(eq(serviceOffering), any())).thenReturn(serviceOffering); + + assertFalse(_vmSnapshotMgr.userVmServiceOfferingNeedsChange(userVm, vmSnapshotVO)); + + verify(_serviceOfferingDao).getComputeOffering(eq(serviceOffering), any()); verify(_vmSnapshotMgr).getVmMapDetails(vmSnapshotVO); - verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture()); + } + + @Test + public void testUserVmServiceOfferingNeedsChangeWhenDynamicCpuDiffersFromSnapshot() { + when(serviceOffering.isDynamic()).thenReturn(true); + when(serviceOffering.getCpu()).thenReturn(2); + when(serviceOffering.getRamSize()).thenReturn(2048); + when(serviceOffering.getSpeed()).thenReturn(1000); + ServiceOfferingVO fromSnapshot = mock(ServiceOfferingVO.class); + when(fromSnapshot.getCpu()).thenReturn(4); + when(fromSnapshot.getRamSize()).thenReturn(2048); + when(fromSnapshot.getSpeed()).thenReturn(1000); + when(_serviceOfferingDao.getComputeOffering(eq(serviceOffering), any())).thenReturn(fromSnapshot); + + assertTrue(_vmSnapshotMgr.userVmServiceOfferingNeedsChange(userVm, vmSnapshotVO)); } @Test diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index 0decef7adf9..9655b797ae9 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -39,22 +39,6 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import org.jetbrains.annotations.NotNull; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.BDDMockito; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockedConstruction; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; @@ -73,10 +57,26 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.BDDMockito; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -110,6 +110,7 @@ import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.UnsupportedServiceException; import com.cloud.host.Host; @@ -182,7 +183,7 @@ public class UnmanagedVMsManagerImplTest { @Spy @InjectMocks - private UnmanagedVMsManagerImpl unmanagedVMsManager = new UnmanagedVMsManagerImpl(); + private UnmanagedVMsManagerImpl unmanagedVMsManager; @Mock private UserVmManager userVmManager; @@ -264,6 +265,14 @@ public class UnmanagedVMsManagerImplTest { private ConfigKey configKeyMockParamsAllowed; @Mock private ConfigKey configKeyMockParamsAllowedList; + @Mock + private Account accountMock; + @Mock + private ServiceOfferingVO serviceOfferingMock; + @Mock + private VMTemplateVO templateMock; + @Mock + private UnmanagedInstanceTO unmanagedInstanceMock; private static final long virtualMachineId = 1L; @@ -390,6 +399,11 @@ public class UnmanagedVMsManagerImplTest { when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine); when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Running); + + when(unmanagedInstanceMock.getCpuCores()).thenReturn(8); + when(unmanagedInstanceMock.getMemory()).thenReturn(4096); + when(serviceOfferingMock.getCpu()).thenReturn(4); + when(serviceOfferingMock.getRamSize()).thenReturn(2048); } @NotNull @@ -467,7 +481,9 @@ public class UnmanagedVMsManagerImplTest { ImportUnmanagedInstanceCmd importUnmanageInstanceCmd = Mockito.mock(ImportUnmanagedInstanceCmd.class); when(importUnmanageInstanceCmd.getName()).thenReturn("SomeInstance"); when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null); - unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd); + } } @Test(expected = InvalidParameterValueException.class) @@ -1496,4 +1512,102 @@ public class UnmanagedVMsManagerImplTest { Assert.assertFalse(params.containsKey(VmDetailConstants.CPU_SPEED)); Assert.assertFalse(params.containsKey(VmDetailConstants.MEMORY)); } + + @Test + public void checkVmResourceLimitsForUnmanagedInstanceImportTestUsesInformationFromHypervisorWhenOfferingIsDynamic() throws Exception { + when(serviceOfferingMock.isDynamic()).thenReturn(true); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForUnmanagedInstanceImport(accountMock, unmanagedInstanceMock, serviceOfferingMock, templateMock, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(unmanagedInstanceMock).getCpuCores(); + verify(unmanagedInstanceMock).getMemory(); + } + } + + @Test + public void checkVmResourceLimitsForUnmanagedInstanceImportTestUsesInformationFromHypervisorWhenVmIsPoweredOn() throws Exception { + when(unmanagedInstanceMock.getPowerState()).thenReturn(UnmanagedInstanceTO.PowerState.PowerOn); + when(serviceOfferingMock.isDynamic()).thenReturn(false); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForUnmanagedInstanceImport(accountMock, unmanagedInstanceMock, serviceOfferingMock, templateMock, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(unmanagedInstanceMock).getCpuCores(); + verify(unmanagedInstanceMock).getMemory(); + } + } + + @Test + public void checkVmResourceLimitsForUnmanagedInstanceImportTestUsesInformationFromOfferingWhenOfferingIsNotDynamicAndVmIsPoweredOff() throws Exception { + when(unmanagedInstanceMock.getPowerState()).thenReturn(UnmanagedInstanceTO.PowerState.PowerOff); + when(serviceOfferingMock.isDynamic()).thenReturn(false); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForUnmanagedInstanceImport(accountMock, unmanagedInstanceMock, serviceOfferingMock, templateMock, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(serviceOfferingMock).getCpu(); + verify(serviceOfferingMock).getRamSize(); + verify(unmanagedInstanceMock, Mockito.never()).getCpuCores(); + verify(unmanagedInstanceMock, Mockito.never()).getMemory(); + } + } + + @Test + public void checkVmResourceLimitsForExternalKvmVmImportTestUsesInformationFromOfferingWhenOfferingIsNotDynamic() throws ResourceAllocationException { + when(serviceOfferingMock.isDynamic()).thenReturn(false); + Map details = new HashMap<>(); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForExternalKvmVmImport(accountMock, serviceOfferingMock, templateMock, details, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(serviceOfferingMock).getCpu(); + verify(serviceOfferingMock).getRamSize(); + verify(unmanagedVMsManager, Mockito.never()).getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details); + verify(unmanagedVMsManager, Mockito.never()).getDetailAsInteger(VmDetailConstants.MEMORY, details); + } + } + + @Test + public void checkVmResourceLimitsForExternalKvmVmImportTestUsesInformationFromDetailsWhenOfferingIsDynamic() throws ResourceAllocationException { + when(serviceOfferingMock.isDynamic()).thenReturn(true); + Map details = new HashMap<>(); + details.put(VmDetailConstants.CPU_NUMBER, "8"); + details.put(VmDetailConstants.MEMORY, "4096"); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForExternalKvmVmImport(accountMock, serviceOfferingMock, templateMock, details, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(unmanagedVMsManager).getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details); + verify(unmanagedVMsManager).getDetailAsInteger(VmDetailConstants.MEMORY, details); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void getDetailAsIntegerTestThrowsInvalidParameterValueExceptionWhenDetailIsNull() { + Map details = new HashMap<>(); + unmanagedVMsManager.getDetailAsInteger("non-existent", details); + } + + @Test(expected = InvalidParameterValueException.class) + public void getDetailAsIntegerTestThrowsInvalidParameterValueExceptionWhenValueIsInvalid() { + Map details = new HashMap<>(); + details.put("key", "not-a-number"); + unmanagedVMsManager.getDetailAsInteger("key", details); + } } diff --git a/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py b/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py index 80d64e8f2d9..93d0d0388ef 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py @@ -244,6 +244,8 @@ class CsNetfilters(object): CsHelper.execute("nft add chain %s %s %s '{ %s }'" % (address_family, table, chain, chain_policy)) if hook == "input" or hook == "output": CsHelper.execute("nft add rule %s %s %s icmp type { echo-request, echo-reply } accept" % (address_family, table, chain)) + elif hook == "forward": + CsHelper.execute("nft add rule %s %s %s ct state established,related accept" % (address_family, table, chain)) def apply_nft_ipv4_rules(self, rules, type): if len(rules) == 0: diff --git a/test/integration/component/maint/test_redundant_router_deployment_planning.py b/test/integration/component/maint/test_redundant_router_deployment_planning.py index 5012fd1f2e9..d10e3ee5a7f 100644 --- a/test/integration/component/maint/test_redundant_router_deployment_planning.py +++ b/test/integration/component/maint/test_redundant_router_deployment_planning.py @@ -398,141 +398,146 @@ class TestRvRDeploymentPlanning(cloudstackTestCase): self.apiclient.updatePod(cmd) self.debug("Enabled first pod for testing..") - # Creating network using the network offering created - self.debug("Creating network with network offering: %s" % - self.network_offering.id) - network = Network.create( - self.apiclient, - self.services["network"], - accountid=self.account.name, - domainid=self.account.domainid, - networkofferingid=self.network_offering.id, - zoneid=self.zone.id - ) - self.debug("Created network with ID: %s" % network.id) + try: + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % network.id) - networks = Network.list( - self.apiclient, - id=network.id, - listall=True - ) - self.assertEqual( - isinstance(networks, list), - True, - "List networks should return a valid response for created network" - ) - nw_response = networks[0] + networks = Network.list( + self.apiclient, + id=network.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List networks should return a valid response for created network" + ) + nw_response = networks[0] - self.debug("Network state: %s" % nw_response.state) - self.assertEqual( - nw_response.state, - "Allocated", - "The network should be in allocated state after creation" - ) - - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - routers, - None, - "Routers should not be spawned when network is in allocated state" - ) - - self.debug("Deploying VM in account: %s" % self.account.name) - - # Spawn an instance in that network - virtual_machine = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering.id, - networkids=[str(network.id)] - ) - self.debug("Deployed VM in network: %s" % network.id) - - vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine.id, - listall=True - ) - self.assertEqual( - isinstance(vms, list), - True, - "List Vms should return a valid list" - ) - vm = vms[0] - self.assertEqual( - vm.state, - "Running", - "Vm should be in running state after deployment" - ) - - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - isinstance(routers, list), - True, - "list router should return Primary and backup routers" - ) - self.assertEqual( - len(routers), - 2, - "Length of the list router should be 2 (Backup & Primary)" - ) - - hosts = Host.list( - self.apiclient, - id=routers[0].hostid, - listall=True - ) - self.assertEqual( - isinstance(hosts, list), - True, - "List host should return a valid data" - ) - first_host = hosts[0] - - hosts = Host.list( - self.apiclient, - id=routers[1].hostid, - listall=True - ) - self.assertEqual( - isinstance(hosts, list), - True, - "List host should return a valid data" - ) - second_host = hosts[0] - - # Checking if the cluster IDs of both routers are different? - self.assertNotEqual( - first_host.clusterid, - second_host.clusterid, - "Both the routers should be in different clusters" - ) - self.debug("Enabling remaining pods if any..") - pods = Pod.list( - self.apiclient, - zoneid=self.zone.id, - listall=True, - allocationstate="Disabled" + self.debug("Network state: %s" % nw_response.state) + self.assertEqual( + nw_response.state, + "Allocated", + "The network should be in allocated state after creation" ) - if pods is not None: - for pod in pods: - cmd = updatePod.updatePodCmd() - cmd.id = pod.id - cmd.allocationstate = 'Enabled' - self.apiclient.updatePod(cmd) + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + routers, + None, + "Routers should not be spawned when network is in allocated state" + ) + + self.debug("Deploying VM in account: %s" % self.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(network.id)] + ) + self.debug("Deployed VM in network: %s" % network.id) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List Vms should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "Vm should be in running state after deployment" + ) + + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + isinstance(routers, list), + True, + "list router should return Primary and backup routers" + ) + self.assertEqual( + len(routers), + 2, + "Length of the list router should be 2 (Backup & Primary)" + ) + + hosts = Host.list( + self.apiclient, + id=routers[0].hostid, + listall=True + ) + self.assertEqual( + isinstance(hosts, list), + True, + "List host should return a valid data" + ) + first_host = hosts[0] + + hosts = Host.list( + self.apiclient, + id=routers[1].hostid, + listall=True + ) + self.assertEqual( + isinstance(hosts, list), + True, + "List host should return a valid data" + ) + second_host = hosts[0] + + # Checking if the cluster IDs of both routers are different? + self.assertNotEqual( + first_host.clusterid, + second_host.clusterid, + "Both the routers should be in different clusters" + ) + finally: + try: + self.debug("Enabling remaining pods if any..") + pods = Pod.list( + self.apiclient, + zoneid=self.zone.id, + listall=True, + allocationstate="Disabled" + ) + + if pods is not None: + for pod in pods: + cmd = updatePod.updatePodCmd() + cmd.id = pod.id + cmd.allocationstate = 'Enabled' + self.apiclient.updatePod(cmd) + except Exception as e: + self.debug("Warning: Exception during pod re-enablement: %s" % e) return # @attr(tags=["advanced", "advancedns"]) @@ -557,7 +562,7 @@ class TestRvRDeploymentPlanning(cloudstackTestCase): # 3. VM should be deployed and in Running state and on the specified # host # 4. There should be two routers (PRIMARY and BACKUP) for this network - # ensure both routers should be on different storage pools + # ensure both routers should be on different hosts self.debug( "Checking if the current zone has multiple active pods in it..") @@ -636,144 +641,150 @@ class TestRvRDeploymentPlanning(cloudstackTestCase): self.apiclient.updateCluster(cmd) self.debug("Enabled first cluster for testing..") - # Creating network using the network offering created - self.debug("Creating network with network offering: %s" % - self.network_offering.id) - network = Network.create( - self.apiclient, - self.services["network"], - accountid=self.account.name, - domainid=self.account.domainid, - networkofferingid=self.network_offering.id, - zoneid=self.zone.id - ) - self.debug("Created network with ID: %s" % network.id) + try: + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % network.id) - networks = Network.list( - self.apiclient, - id=network.id, - listall=True - ) - self.assertEqual( - isinstance(networks, list), - True, - "List networks should return a valid response for created network" - ) - nw_response = networks[0] + networks = Network.list( + self.apiclient, + id=network.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List networks should return a valid response for created network" + ) + nw_response = networks[0] - self.debug("Network state: %s" % nw_response.state) - self.assertEqual( - nw_response.state, - "Allocated", - "The network should be in allocated state after creation" - ) + self.debug("Network state: %s" % nw_response.state) + self.assertEqual( + nw_response.state, + "Allocated", + "The network should be in allocated state after creation" + ) - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - routers, - None, - "Routers should not be spawned when network is in allocated state" - ) - - self.debug("Retrieving the list of hosts in the cluster") - hosts = Host.list( - self.apiclient, - clusterid=enabled_cluster.id, - listall=True - ) - self.assertEqual( - isinstance(hosts, list), - True, - "List hosts should not return an empty response" - ) - host = hosts[0] - - self.debug("Deploying VM in account: %s" % self.account.name) - - # Spawn an instance in that network - virtual_machine = VirtualMachine.create( + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering.id, - networkids=[str(network.id)], - hostid=host.id - ) - self.debug("Deployed VM in network: %s" % network.id) - - vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine.id, + networkid=network.id, listall=True ) - self.assertEqual( - isinstance(vms, list), - True, - "List Vms should return a valid list" - ) - vm = vms[0] - self.assertEqual( - vm.state, - "Running", - "Vm should be in running state after deployment" - ) + self.assertEqual( + routers, + None, + "Routers should not be spawned when network is in allocated state" + ) - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( + self.debug("Retrieving the list of hosts in the cluster") + hosts = Host.list( self.apiclient, - networkid=network.id, + clusterid=enabled_cluster.id, listall=True ) - self.assertEqual( - isinstance(routers, list), - True, - "list router should return Primary and backup routers" - ) - self.assertEqual( - len(routers), - 2, - "Length of the list router should be 2 (Backup & Primary)" - ) - self.assertNotEqual( - routers[0].hostid, - routers[1].hostid, - "Both the routers should be in different storage pools" - ) - self.debug("Enabling remaining pods if any..") - pods = Pod.list( - self.apiclient, - zoneid=self.zone.id, - listall=True, - allocationstate="Disabled" + self.assertEqual( + isinstance(hosts, list), + True, + "List hosts should not return an empty response" + ) + host = hosts[0] + + self.debug("Deploying VM in account: %s" % self.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(network.id)], + hostid=host.id + ) + self.debug("Deployed VM in network: %s" % network.id) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List Vms should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "Vm should be in running state after deployment" + ) + + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + isinstance(routers, list), + True, + "list router should return Primary and backup routers" ) - if pods is not None: - for pod in pods: - cmd = updatePod.updatePodCmd() - cmd.id = pod.id - cmd.allocationstate = 'Enabled' - self.apiclient.updatePod(cmd) - - clusters = Cluster.list( - self.apiclient, - allocationstate="Disabled", - podid=enabled_pod.id, - listall=True + self.assertEqual( + len(routers), + 2, + "Length of the list router should be 2 (Backup & Primary)" + ) + self.assertNotEqual( + routers[0].hostid, + routers[1].hostid, + "Both the routers should be in different hosts" ) + finally: + try: + self.debug("Enabling remaining pods if any..") + pods = Pod.list( + self.apiclient, + zoneid=self.zone.id, + listall=True, + allocationstate="Disabled" + ) + if pods is not None: + for pod in pods: + cmd = updatePod.updatePodCmd() + cmd.id = pod.id + cmd.allocationstate = 'Enabled' + self.apiclient.updatePod(cmd) - if clusters is not None: - for cluster in clusters: - cmd = updateCluster.updateClusterCmd() - cmd.id = cluster.id - cmd.allocationstate = 'Enabled' - self.apiclient.updateCluster(cmd) + clusters = Cluster.list( + self.apiclient, + allocationstate="Disabled", + podid=enabled_pod.id, + listall=True + ) + + if clusters is not None: + for cluster in clusters: + cmd = updateCluster.updateClusterCmd() + cmd.id = cluster.id + cmd.allocationstate = 'Enabled' + self.apiclient.updateCluster(cmd) + except Exception as e: + self.debug("Warning: Exception during resource re-enablement: %s" % e) return + # @attr(tags=["advanced", "advancedns", "ssh"]) @attr(tags=["TODO"]) def test_RvR_multihosts(self): @@ -874,140 +885,145 @@ class TestRvRDeploymentPlanning(cloudstackTestCase): self.apiclient.updateCluster(cmd) self.debug("Enabled first cluster for testing..") - # Creating network using the network offering created - self.debug("Creating network with network offering: %s" % - self.network_offering.id) - network = Network.create( - self.apiclient, - self.services["network"], - accountid=self.account.name, - domainid=self.account.domainid, - networkofferingid=self.network_offering.id, - zoneid=self.zone.id - ) - self.debug("Created network with ID: %s" % network.id) + try: + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % network.id) - networks = Network.list( - self.apiclient, - id=network.id, - listall=True - ) - self.assertEqual( - isinstance(networks, list), - True, - "List networks should return a valid response for created network" - ) - nw_response = networks[0] + networks = Network.list( + self.apiclient, + id=network.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List networks should return a valid response for created network" + ) + nw_response = networks[0] - self.debug("Network state: %s" % nw_response.state) - self.assertEqual( - nw_response.state, - "Allocated", - "The network should be in allocated state after creation" - ) - - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - routers, - None, - "Routers should not be spawned when network is in allocated state" - ) - - self.debug("Retrieving the list of hosts in the cluster") - hosts = Host.list( - self.apiclient, - clusterid=enabled_cluster.id, - listall=True - ) - self.assertEqual( - isinstance(hosts, list), - True, - "List hosts should not return an empty response" - ) - host = hosts[0] - - self.debug("Deploying VM in account: %s" % self.account.name) - - # Spawn an instance in that network - virtual_machine = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering.id, - networkids=[str(network.id)], - hostid=host.id - ) - self.debug("Deployed VM in network: %s" % network.id) - - vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine.id, - listall=True - ) - self.assertEqual( - isinstance(vms, list), - True, - "List Vms should return a valid list" - ) - vm = vms[0] - self.assertEqual( - vm.state, - "Running", - "Vm should be in running state after deployment" - ) - - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - isinstance(routers, list), - True, - "list router should return Primary and backup routers" - ) - self.assertEqual( - len(routers), - 2, - "Length of the list router should be 2 (Backup & Primary)" - ) - self.assertNotEqual( - routers[0].hostid, - routers[1].hostid, - "Both the routers should be in different hosts" - ) - self.debug("Enabling remaining pods if any..") - pods = Pod.list( - self.apiclient, - zoneid=self.zone.id, - listall=True, - allocationstate="Disabled" + self.debug("Network state: %s" % nw_response.state) + self.assertEqual( + nw_response.state, + "Allocated", + "The network should be in allocated state after creation" ) - if pods is not None: - for pod in pods: - cmd = updatePod.updatePodCmd() - cmd.id = pod.id - cmd.allocationstate = 'Enabled' - self.apiclient.updatePod(cmd) + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + routers, + None, + "Routers should not be spawned when network is in allocated state" + ) - clusters = Cluster.list( - self.apiclient, - allocationstate="Disabled", - podid=enabled_pod.id, - listall=True + self.debug("Retrieving the list of hosts in the cluster") + hosts = Host.list( + self.apiclient, + clusterid=enabled_cluster.id, + listall=True + ) + self.assertEqual( + isinstance(hosts, list), + True, + "List hosts should not return an empty response" + ) + host = hosts[0] + + self.debug("Deploying VM in account: %s" % self.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(network.id)], + hostid=host.id + ) + self.debug("Deployed VM in network: %s" % network.id) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List Vms should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "Vm should be in running state after deployment" + ) + + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + isinstance(routers, list), + True, + "list router should return Primary and backup routers" + ) + self.assertEqual( + len(routers), + 2, + "Length of the list router should be 2 (Backup & Primary)" + ) + self.assertNotEqual( + routers[0].hostid, + routers[1].hostid, + "Both the routers should be in different hosts" ) - if clusters is not None: - for cluster in clusters: - cmd = updateCluster.updateClusterCmd() - cmd.id = cluster.id - cmd.allocationstate = 'Enabled' - self.apiclient.updateCluster(cmd) + finally: + try: + self.debug("Enabling remaining pods if any..") + pods = Pod.list( + self.apiclient, + zoneid=self.zone.id, + listall=True, + allocationstate="Disabled" + ) + + if pods is not None: + for pod in pods: + cmd = updatePod.updatePodCmd() + cmd.id = pod.id + cmd.allocationstate = 'Enabled' + self.apiclient.updatePod(cmd) + + clusters = Cluster.list( + self.apiclient, + allocationstate="Disabled", + podid=enabled_pod.id, + listall=True + ) + if clusters is not None: + for cluster in clusters: + cmd = updateCluster.updateClusterCmd() + cmd.id = cluster.id + cmd.allocationstate = 'Enabled' + self.apiclient.updateCluster(cmd) + except Exception as e: + self.debug("Warning: Exception during resource re-enablement: %s" % e) return diff --git a/test/integration/smoke/test_public_ip_range.py b/test/integration/smoke/test_public_ip_range.py index 19edc4c164f..997716caaaf 100644 --- a/test/integration/smoke/test_public_ip_range.py +++ b/test/integration/smoke/test_public_ip_range.py @@ -286,20 +286,25 @@ class TestDedicatePublicIPRange(cloudstackTestCase): cmd.allocationstate = 'Disabled' self.apiclient.updateZone(cmd) - # Delete System VM and IP range, so System VM can get IP from original ranges - self.debug("Destroying System VM: %s" % systemvm_id) - cmd = destroySystemVm.destroySystemVmCmd() - cmd.id = systemvm_id - self.apiclient.destroySystemVm(cmd) + try: + # Delete System VM and IP range, so System VM can get IP from original ranges + self.debug("Destroying System VM: %s" % systemvm_id) + cmd = destroySystemVm.destroySystemVmCmd() + cmd.id = systemvm_id + self.apiclient.destroySystemVm(cmd) - domain_id = self.public_ip_range.vlan.domainid - self.public_ip_range.delete(self.apiclient) + domain_id = self.public_ip_range.vlan.domainid + self.public_ip_range.delete(self.apiclient) - # Enable Zone - cmd = updateZone.updateZoneCmd() - cmd.id = self.zone.id - cmd.allocationstate = 'Enabled' - self.apiclient.updateZone(cmd) + finally: + # Enable Zone + try: + cmd = updateZone.updateZoneCmd() + cmd.id = self.zone.id + cmd.allocationstate = 'Enabled' + self.apiclient.updateZone(cmd) + except Exception as e: + self.debug("Warning: Exception during zone re-enablement in base_system_vm: %s" % e) # Wait for System VM to start and check System VM public IP systemvm_id = self.wait_for_system_vm_start( @@ -399,18 +404,23 @@ class TestDedicatePublicIPRange(cloudstackTestCase): cmd.allocationstate = 'Disabled' self.apiclient.updateZone(cmd) - # Delete System VM and IP range, so System VM can get IP from original ranges - if system_vms: - for v in system_vms: - self.debug("Destroying System VM: %s" % v.id) - cmd = destroySystemVm.destroySystemVmCmd() - cmd.id = v.id - self.apiclient.destroySystemVm(cmd) + try: + # Delete System VM and IP range, so System VM can get IP from original ranges + if system_vms: + for v in system_vms: + self.debug("Destroying System VM: %s" % v.id) + cmd = destroySystemVm.destroySystemVmCmd() + cmd.id = v.id + self.apiclient.destroySystemVm(cmd) - self.public_ip_range.delete(self.apiclient) + self.public_ip_range.delete(self.apiclient) - # Enable Zone - cmd = updateZone.updateZoneCmd() - cmd.id = self.zone.id - cmd.allocationstate = 'Enabled' - self.apiclient.updateZone(cmd) + finally: + # Enable Zone + try: + cmd = updateZone.updateZoneCmd() + cmd.id = self.zone.id + cmd.allocationstate = 'Enabled' + self.apiclient.updateZone(cmd) + except Exception as e: + self.debug("Warning: Exception during zone re-enablement in delete_range: %s" % e)