diff --git a/api/src/com/cloud/api/commands/CreateNetworkCmd.java b/api/src/com/cloud/api/commands/CreateNetworkCmd.java index 4570a05ddbe..31a6069b572 100644 --- a/api/src/com/cloud/api/commands/CreateNetworkCmd.java +++ b/api/src/com/cloud/api/commands/CreateNetworkCmd.java @@ -30,6 +30,7 @@ import com.cloud.api.response.NetworkResponse; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; import com.cloud.offering.NetworkOffering; @@ -208,8 +209,8 @@ public class CreateNetworkCmd extends BaseCmd { } @Override - public void execute() throws InsufficientCapacityException, ConcurrentOperationException{ - // an exception thrown by createNetwork() will be caught by the dispatcher. + // an exception thrown by createNetwork() will be caught by the dispatcher. + public void execute() throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException{ Network result = _networkService.createNetwork(this); if (result != null) { NetworkResponse response = _responseGenerator.createNetworkResponse(result); diff --git a/api/src/com/cloud/api/commands/CreateVlanIpRangeCmd.java b/api/src/com/cloud/api/commands/CreateVlanIpRangeCmd.java index 9a0ba7f4724..e31bc4c62a6 100644 --- a/api/src/com/cloud/api/commands/CreateVlanIpRangeCmd.java +++ b/api/src/com/cloud/api/commands/CreateVlanIpRangeCmd.java @@ -30,6 +30,7 @@ import com.cloud.api.response.VlanIpRangeResponse; import com.cloud.dc.Vlan; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; @@ -159,7 +160,7 @@ public class CreateVlanIpRangeCmd extends BaseCmd { } @Override - public void execute() throws ResourceUnavailableException{ + public void execute() throws ResourceUnavailableException, ResourceAllocationException{ try { Vlan result = _configService.createVlanAndPublicIpRange(this); if (result != null) { diff --git a/api/src/com/cloud/api/response/AccountResponse.java b/api/src/com/cloud/api/response/AccountResponse.java index 1d9de5c4229..6d628067d45 100755 --- a/api/src/com/cloud/api/response/AccountResponse.java +++ b/api/src/com/cloud/api/response/AccountResponse.java @@ -97,6 +97,25 @@ public class AccountResponse extends BaseResponse { @SerializedName("vmrunning") @Param(description="the total number of virtual machines running for this account") private Integer vmRunning; + + @SerializedName("projectlimit") @Param(description="the total number of projects the account can own") + private String projectLimit; + + @SerializedName("projecttotal") @Param(description="the total number of projects being administrated by this account") + private Long projectTotal; + + @SerializedName("projectavailable") @Param(description="the total number of projects available for administration by this account") + private String projectAvailable; + + @SerializedName("networklimit") @Param(description="the total number of networks the account can own") + private String networkLimit; + + @SerializedName("networktotal") @Param(description="the total number of networks owned by account") + private Long networkTotal; + + @SerializedName("networkavailable") @Param(description="the total number of networks available to be created for this account") + private String networkAvailable; + @SerializedName(ApiConstants.STATE) @Param(description="the state of the account") private String state; @@ -113,218 +132,110 @@ public class AccountResponse extends BaseResponse { @SerializedName(ApiConstants.ACCOUNT_DETAILS) @Param(description="details for the account") private Map details; - public Long getId() { - return id.getValue(); - } - public void setId(Long id) { this.id.setValue(id); } - public String getName() { - return name; - } - public void setName(String name) { this.name = name; } - public Short getAccountType() { - return accountType; - } - public void setAccountType(Short accountType) { this.accountType = accountType; } - public Long getDomainId() { - return domainId.getValue(); - } - public void setDomainId(Long domainId) { this.domainId.setValue(domainId); } - public String getDomainName() { - return domainName; - } - public void setDomainName(String domainName) { this.domainName = domainName; } - public Long getBytesReceived() { - return bytesReceived; - } - public void setBytesReceived(Long bytesReceived) { this.bytesReceived = bytesReceived; } - public Long getBytesSent() { - return bytesSent; - } - public void setBytesSent(Long bytesSent) { this.bytesSent = bytesSent; } - public String getVmLimit() { - return vmLimit; - } - public void setVmLimit(String vmLimit) { this.vmLimit = vmLimit; } - public Long getVmTotal() { - return vmTotal; - } - public void setVmTotal(Long vmTotal) { this.vmTotal = vmTotal; } - public String getVmAvailable() { - return vmAvailable; - } - public void setVmAvailable(String vmAvailable) { this.vmAvailable = vmAvailable; } - public String getIpLimit() { - return ipLimit; - } - public void setIpLimit(String ipLimit) { this.ipLimit = ipLimit; } - public Long getIpTotal() { - return ipTotal; - } - public void setIpTotal(Long ipTotal) { this.ipTotal = ipTotal; } - public String getIpAvailable() { - return ipAvailable; - } - public void setIpAvailable(String ipAvailable) { this.ipAvailable = ipAvailable; } - public String getVolumeLimit() { - return volumeLimit; - } - public void setVolumeLimit(String volumeLimit) { this.volumeLimit = volumeLimit; } - public Long getVolumeTotal() { - return volumeTotal; - } - public void setVolumeTotal(Long volumeTotal) { this.volumeTotal = volumeTotal; } - public String getVolumeAvailable() { - return volumeAvailable; - } - public void setVolumeAvailable(String volumeAvailable) { this.volumeAvailable = volumeAvailable; } - public String getSnapshotLimit() { - return snapshotLimit; - } - public void setSnapshotLimit(String snapshotLimit) { this.snapshotLimit = snapshotLimit; } - public Long getSnapshotTotal() { - return snapshotTotal; - } - public void setSnapshotTotal(Long snapshotTotal) { this.snapshotTotal = snapshotTotal; } - public String getSnapshotAvailable() { - return snapshotAvailable; - } - public void setSnapshotAvailable(String snapshotAvailable) { this.snapshotAvailable = snapshotAvailable; } - public String getTemplateLimit() { - return templateLimit; - } - public void setTemplateLimit(String templateLimit) { this.templateLimit = templateLimit; } - public Long getTemplateTotal() { - return templateTotal; - } - public void setTemplateTotal(Long templateTotal) { this.templateTotal = templateTotal; } - public String getTemplateAvailable() { - return templateAvailable; - } - public void setTemplateAvailable(String templateAvailable) { this.templateAvailable = templateAvailable; } - public Integer getVmStopped() { - return vmStopped; - } - public void setVmStopped(Integer vmStopped) { this.vmStopped = vmStopped; } - public Integer getVmRunning() { - return vmRunning; - } - public void setVmRunning(Integer vmRunning) { this.vmRunning = vmRunning; } - public String getState() { - return state; - } - public void setState(String state) { this.state = state; } - public Boolean getCleanupRequired() { - return cleanupRequired; - } - public void setCleanupRequired(Boolean cleanupRequired) { this.cleanupRequired = cleanupRequired; } - public List getUsers() { - return this.users; - } - public void setUsers(List users) { this.users = users; } @@ -336,8 +247,28 @@ public class AccountResponse extends BaseResponse { public void setDetails(Map details) { this.details = details; } - - public Map getDetails() { - return details; + + public void setProjectLimit(String projectLimit) { + this.projectLimit = projectLimit; + } + + public void setProjectTotal(Long projectTotal) { + this.projectTotal = projectTotal; + } + + public void setProjectAvailable(String projectAvailable) { + this.projectAvailable = projectAvailable; + } + + public void setNetworkLimit(String networkLimit) { + this.networkLimit = networkLimit; + } + + public void setNetworkTotal(Long networkTotal) { + this.networkTotal = networkTotal; + } + + public void setNetworkAvailable(String networkAvailable) { + this.networkAvailable = networkAvailable; } } diff --git a/api/src/com/cloud/configuration/ConfigurationService.java b/api/src/com/cloud/configuration/ConfigurationService.java index ce808410ace..553fb16acd5 100644 --- a/api/src/com/cloud/configuration/ConfigurationService.java +++ b/api/src/com/cloud/configuration/ConfigurationService.java @@ -47,6 +47,7 @@ import com.cloud.dc.Pod; import com.cloud.dc.Vlan; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Networks.TrafficType; import com.cloud.offering.DiskOffering; @@ -215,10 +216,11 @@ public interface ConfigurationService { * @param gateway * @param startIP * @param endIP + * @throws ResourceAllocationException TODO * @throws * @return The new Vlan object */ - Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException; + Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, ResourceAllocationException; boolean deleteVlanIpRange(DeleteVlanIpRangeCmd cmd); diff --git a/api/src/com/cloud/configuration/Resource.java b/api/src/com/cloud/configuration/Resource.java index e867d859bce..5d01964b839 100644 --- a/api/src/com/cloud/configuration/Resource.java +++ b/api/src/com/cloud/configuration/Resource.java @@ -27,7 +27,8 @@ public interface Resource { volume("volume", 2, ResourceOwnerType.Account, ResourceOwnerType.Domain), snapshot("snapshot", 3, ResourceOwnerType.Account, ResourceOwnerType.Domain), template("template", 4, ResourceOwnerType.Account, ResourceOwnerType.Domain), - project("project", 5, ResourceOwnerType.Account, ResourceOwnerType.Domain); + project("project", 5, ResourceOwnerType.Account, ResourceOwnerType.Domain), + network("network", 6, ResourceOwnerType.Account, ResourceOwnerType.Domain); private String name; private ResourceOwnerType[] supportedOwners; diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index 4d7e6b68192..c4be2177545 100755 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -57,7 +57,7 @@ public interface NetworkService { boolean disassociateIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; - Network createNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException; + Network createNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException; List searchForNetworks(ListNetworksCmd cmd); diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index ff176499b21..28fe457ee90 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -279,7 +279,7 @@ public class ApiResponseHelper implements ResponseGenerator { Long ips = ipLimit - ipTotal; // check how many free ips are left, and if it's less than max allowed number of ips from account - use this -// value + // value Long ipsLeft = ApiDBUtils.countFreePublicIps(); boolean unlimited = true; if (ips.longValue() > ipsLeft.longValue()) { @@ -341,7 +341,25 @@ public class ApiResponseHelper implements ResponseGenerator { accountResponse.setVmStopped(vmStopped); accountResponse.setVmRunning(vmRunning); accountResponse.setObjectName("account"); - + + //get resource limits for projects + Long projectLimit = ApiDBUtils.findCorrectResourceLimit(ResourceType.project, account.getId()); + String projectLimitDisplay = (accountIsAdmin || projectLimit == -1) ? "Unlimited" : String.valueOf(projectLimit); + Long projectTotal = ApiDBUtils.getResourceCount(ResourceType.project, account.getId()); + String projectAvail = (accountIsAdmin || projectLimit == -1) ? "Unlimited" : String.valueOf(projectLimit - projectTotal); + accountResponse.setProjectLimit(projectLimitDisplay); + accountResponse.setProjectTotal(projectTotal); + accountResponse.setProjectAvailable(projectAvail); + + //get resource limits for networks + Long networkLimit = ApiDBUtils.findCorrectResourceLimit(ResourceType.network, account.getId()); + String networkLimitDisplay = (accountIsAdmin || networkLimit == -1) ? "Unlimited" : String.valueOf(networkLimit); + Long networkTotal = ApiDBUtils.getResourceCount(ResourceType.network, account.getId()); + String networkAvail = (accountIsAdmin || networkLimit == -1) ? "Unlimited" : String.valueOf(networkLimit - networkTotal); + accountResponse.setNetworkLimit(networkLimitDisplay); + accountResponse.setNetworkTotal(networkTotal); + accountResponse.setNetworkAvailable(networkAvail); + // adding all the users for an account as part of the response obj List usersForAccount = ApiDBUtils.listUsersByAccount(account.getAccountId()); List userResponseList = new ArrayList(); diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 9b30e60fe37..6d7f7641de2 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -78,7 +78,7 @@ public enum Config { CopyVolumeWait("Storage", StorageManager.class, Integer.class, "copy.volume.wait", "10800", "In second, timeout for copy volume command", null), CreatePrivateTemplateFromVolumeWait("Storage", UserVmManager.class, Integer.class, "create.private.template.from.volume.wait", "10800", "In second, timeout for CreatePrivateTemplateFromVolumeCommand", null), CreatePrivateTemplateFromSnapshotWait("Storage", UserVmManager.class, Integer.class, "create.private.template.from.snapshot.wait", "10800", "In second, timeout for CreatePrivateTemplateFromSnapshotCommand", null), - BackupSnapshotWait( + BackupSnapshotWait( "Storage", StorageManager.class, Integer.class, "backup.snapshot.wait", "21600", "In second, timeout for BackupSnapshotCommand", null), // Network @@ -300,6 +300,8 @@ public enum Config { DefaultMaxAccountTemplates("Account Defaults", ManagementServer.class, Long.class, "max.account.templates", "20", "The default maximum number of templates that can be deployed for an account", null), DefaultMaxAccountSnapshots("Account Defaults", ManagementServer.class, Long.class, "max.account.snapshots", "20", "The default maximum number of snapshots that can be created for an account", null), DefaultMaxAccountVolumes("Account Defaults", ManagementServer.class, Long.class, "max.account.volumes", "20", "The default maximum number of volumes that can be created for an account", null), + DefaultMaxAccountNetworks("Account Defaults", ManagementServer.class, Long.class, "max.account.networks", "20", "The default maximum number of networks that can be created for an account", null), + ResourceCountCheckInterval("Advanced", ManagementServer.class, Long.class, "resourcecount.check.interval", "0", "Time (in seconds) to wait before retrying resource count check task. Default is 0 which is to never run the task", "Seconds"), DirectAgentLoadSize("Advanced", ManagementServer.class, Integer.class, "direct.agent.load.size", "16", "The number of direct agents to load each time", null), @@ -320,6 +322,7 @@ public enum Config { DefaultMaxProjectTemplates("Project Defaults", ManagementServer.class, Long.class, "max.project.templates", "20", "The default maximum number of templates that can be deployed for a project", null), DefaultMaxProjectSnapshots("Project Defaults", ManagementServer.class, Long.class, "max.project.snapshots", "20", "The default maximum number of snapshots that can be created for a project", null), DefaultMaxProjectVolumes("Project Defaults", ManagementServer.class, Long.class, "max.project.volumes", "20", "The default maximum number of volumes that can be created for a project", null), + DefaultMaxProjectNetworks("Project Defaults", ManagementServer.class, Long.class, "max.project.networks", "20", "The default maximum number of networks that can be created for a project", null), ProjectInviteRequired("Project Defaults", ManagementServer.class, Boolean.class, "project.invite.required", "false", "If invitation confirmation is required when add account to project. Default value is false", null), ProjectInvitationExpirationTime("Project Defaults", ManagementServer.class, Long.class, "project.invite.timeout", "86400", "Invitation expiration time (in seconds). Default is 1 day - 86400 seconds", null), diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 200e015360d..337279d76c7 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -98,6 +98,7 @@ import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; @@ -2063,7 +2064,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura @Override @DB @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_CREATE, eventDescription = "creating vlan ip range", async = false) - public Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { + public Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, ResourceAllocationException { Long zoneId = cmd.getZoneId(); Long podId = cmd.getPodId(); String startIP = cmd.getStartIp(); @@ -2279,13 +2280,6 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura if (associateIpRangeToAccount) { _networkMgr.associateIpAddressListToAccount(userId, account.getId(), zoneId, vlan.getId(), network); - if (network == null) { - List networks = _networkMgr.getIsolatedNetworksOwnedByAccountInZone(zoneId, account); - network = networks.get(0); - } - if (network == null) { - throw new CloudRuntimeException("Failed to associate vlan to the account id=" + account.getId() + ", default network failed to create"); - } } txn.commit(); diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 6cff9ee8654..1093bdec666 100755 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -35,6 +35,7 @@ import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.Network.Capability; @@ -168,9 +169,10 @@ public interface NetworkManager extends NetworkService { boolean destroyNetwork(long networkId, ReservationContext context); Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, String networkDomain, Account owner, boolean isSecurityGroupEnabled, Long domainId, - PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess) throws ConcurrentOperationException, InsufficientCapacityException; + PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; /** + * @throws ResourceAllocationException TODO * @throws InsufficientCapacityException * Associates an ip address list to an account. The list of ip addresses are all addresses associated * with the @@ -183,7 +185,7 @@ public interface NetworkManager extends NetworkService { * @throws */ boolean associateIpAddressListToAccount(long userId, long accountId, long zoneId, Long vlanId, Network networkToAssociateWith) throws InsufficientCapacityException, ConcurrentOperationException, - ResourceUnavailableException; + ResourceUnavailableException, ResourceAllocationException; Nic getNicInNetwork(long vmId, long networkId); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 9959cc76303..37233f19710 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -2232,7 +2232,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag @Override @DB @ActionEvent(eventType = EventTypes.EVENT_NETWORK_CREATE, eventDescription = "creating network") - public Network createNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException { + public Network createNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException { Long networkOfferingId = cmd.getNetworkOfferingId(); String gateway = cmd.getGateway(); String startIP = cmd.getStartIp(); @@ -2469,17 +2469,23 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag @Override @DB public Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, String networkDomain, Account owner, boolean isSecurityGroupEnabled, - Long domainId, PhysicalNetwork pNtwk, long zoneId, ACLType aclType, Boolean subdomainAccess) throws ConcurrentOperationException, InsufficientCapacityException { + Long domainId, PhysicalNetwork pNtwk, long zoneId, ACLType aclType, Boolean subdomainAccess) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { - NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId); + NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); // this method supports only guest network creation - if (networkOffering.getTrafficType() != TrafficType.Guest) { + if (ntwkOff.getTrafficType() != TrafficType.Guest) { s_logger.warn("Only guest networks can be created using this method"); return null; } + + boolean updateResourceCount = (!ntwkOff.getSpecifyVlan() && aclType == ACLType.Account); + //check resource limits + if (updateResourceCount) { + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.network); + } // Validate network offering - if (networkOffering.getState() != NetworkOffering.State.Enabled) { + if (ntwkOff.getState() != NetworkOffering.State.Enabled) { // see NetworkOfferingVO InvalidParameterValueException ex = new InvalidParameterValueException("Can't use specified network offering id as its stat is not " + NetworkOffering.State.Enabled); ex.addProxyObject("network_offerings", networkOfferingId, "networkOfferingId"); @@ -2497,6 +2503,11 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag // Validate zone DataCenterVO zone = _dcDao.findById(zoneId); 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 List guestNetworks = _networksDao.listByZoneAndTrafficType(zone.getId(), TrafficType.Guest); if (!guestNetworks.isEmpty()) { @@ -2504,16 +2515,11 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } // if zone is basic, only Shared network offerings w/o source nat service are allowed - if (!(networkOffering.getGuestType() == GuestType.Shared && !areServicesSupportedByNetworkOffering(networkOffering.getId(), Service.SourceNat))) { + if (!(ntwkOff.getGuestType() == GuestType.Shared && !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"); } - // 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 (domainId == null || domainId != Domain.ROOT_DOMAIN) { throw new InvalidParameterValueException("Guest network in Basic zone should be dedicated to ROOT domain"); } @@ -2535,8 +2541,8 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } else if (zone.getNetworkType() == NetworkType.Advanced) { if (zone.isSecurityGroupEnabled()) { // Only Account specific Isolated network with sourceNat service disabled are allowed in security group -// enabled zone - boolean allowCreation = (networkOffering.getGuestType() == GuestType.Isolated && !areServicesSupportedByNetworkOffering(networkOffering.getId(), Service.SourceNat)); + // enabled zone + boolean allowCreation = (ntwkOff.getGuestType() == GuestType.Isolated && !areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)); if (!allowCreation) { throw new InvalidParameterValueException("Only Account specific Isolated network with sourceNat service disabled are allowed in security group enabled zone"); } @@ -2545,7 +2551,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag // VlanId can be specified only when network offering supports it boolean vlanSpecified = (vlanId != null); - if (vlanSpecified != networkOffering.getSpecifyVlan()) { + if (vlanSpecified != ntwkOff.getSpecifyVlan()) { if (vlanSpecified) { throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false"); } else { @@ -2598,10 +2604,10 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } // 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 + // limitation, remove after we introduce support for multiple ip ranges // with different Cidrs for the same Shared network - boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced && networkOffering.getTrafficType() == TrafficType.Guest - && (networkOffering.getGuestType() == GuestType.Shared || (networkOffering.getGuestType() == GuestType.Isolated && !areServicesSupportedByNetworkOffering(networkOffering.getId(), Service.SourceNat))); + boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced && ntwkOff.getTrafficType() == TrafficType.Guest + && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated && !areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))); if (cidr == null && cidrRequired) { throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask are required when create network of type " + Network.GuestType.Shared + " and network of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled"); @@ -2613,7 +2619,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } // Check if cidr is RFC1918 compliant if the network is Guest Isolated - if (cidr != null && networkOffering.getGuestType() == Network.GuestType.Isolated && networkOffering.getTrafficType() == TrafficType.Guest) { + if (cidr != null && ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) { if (!NetUtils.validateGuestCidr(cidr)) { throw new InvalidParameterValueException("Virtual Guest Cidr " + cidr + " is not RFC1918 compliant"); } @@ -2644,7 +2650,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } } - List networks = setupNetwork(owner, networkOffering, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccess); + List networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccess); Network network = null; if (networks == null || networks.isEmpty()) { @@ -2663,6 +2669,10 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag network = networks.get(0); } } + + if (updateResourceCount) { + _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.network); + } txn.commit(); UserContext.current().setEventDetails("Network Id: " + network.getId()); @@ -3719,7 +3729,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag @Override @DB public boolean associateIpAddressListToAccount(long userId, long accountId, long zoneId, Long vlanId, Network network) throws InsufficientCapacityException, ConcurrentOperationException, - ResourceUnavailableException { + ResourceUnavailableException, ResourceAllocationException { Account owner = _accountMgr.getActiveAccountById(accountId); boolean createNetwork = false; diff --git a/server/src/com/cloud/network/dao/NetworkDao.java b/server/src/com/cloud/network/dao/NetworkDao.java index 561b48c8d54..f918e3bd59a 100644 --- a/server/src/com/cloud/network/dao/NetworkDao.java +++ b/server/src/com/cloud/network/dao/NetworkDao.java @@ -95,5 +95,7 @@ public interface NetworkDao extends GenericDao { void setCheckForGc(long networkId); int getNetworkCountByNetworkOffId(long networkOfferingId); + + long countNetworksUserCanCreate(long ownerId); } diff --git a/server/src/com/cloud/network/dao/NetworkDaoImpl.java b/server/src/com/cloud/network/dao/NetworkDaoImpl.java index 080307bcb4b..2077883ad45 100644 --- a/server/src/com/cloud/network/dao/NetworkDaoImpl.java +++ b/server/src/com/cloud/network/dao/NetworkDaoImpl.java @@ -24,6 +24,7 @@ import java.util.Random; import javax.ejb.Local; import javax.persistence.TableGenerator; +import com.cloud.acl.ControlledEntity.ACLType; import com.cloud.network.Network; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; @@ -35,6 +36,8 @@ import com.cloud.network.NetworkVO; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.Mode; import com.cloud.network.Networks.TrafficType; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDaoImpl; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; @@ -61,6 +64,7 @@ public class NetworkDaoImpl extends GenericDaoBase implements N final GenericSearchBuilder CountByOfferingId; final SearchBuilder PhysicalNetworkSearch; final SearchBuilder SecurityGroupSearch; + final GenericSearchBuilder NetworksRegularUserCanCreateSearch; private final GenericSearchBuilder NetworksCount; @@ -68,6 +72,7 @@ public class NetworkDaoImpl extends GenericDaoBase implements N NetworkDomainDaoImpl _domainsDao = ComponentLocator.inject(NetworkDomainDaoImpl.class); NetworkOpDaoImpl _opDao = ComponentLocator.inject(NetworkOpDaoImpl.class); NetworkServiceMapDaoImpl _ntwkSvcMap = ComponentLocator.inject(NetworkServiceMapDaoImpl.class); + NetworkOfferingDaoImpl _ntwkOffDao = ComponentLocator.inject(NetworkOfferingDaoImpl.class); final TableGenerator _tgMacAddress; @@ -145,6 +150,18 @@ public class NetworkDaoImpl extends GenericDaoBase implements N NetworksCount.select(null, Func.COUNT, NetworksCount.entity().getId()); NetworksCount.and("networkOfferingId", NetworksCount.entity().getNetworkOfferingId(), SearchCriteria.Op.EQ); NetworksCount.done(); + + NetworksRegularUserCanCreateSearch = createSearchBuilder(Long.class); + NetworksRegularUserCanCreateSearch.and("aclType", NetworksRegularUserCanCreateSearch.entity().getAclType(), Op.EQ); + NetworksRegularUserCanCreateSearch.select(null, Func.COUNT, NetworksRegularUserCanCreateSearch.entity().getId()); + SearchBuilder join4 = _accountsDao.createSearchBuilder(); + join4.and("account", join4.entity().getAccountId(), Op.EQ); + join4.and("isOwner", join4.entity().isOwner(), Op.EQ); + NetworksRegularUserCanCreateSearch.join("accounts", join4, NetworksRegularUserCanCreateSearch.entity().getId(), join4.entity().getNetworkId(), JoinBuilder.JoinType.INNER); + SearchBuilder join5 = _ntwkOffDao.createSearchBuilder(); + join5.and("specifyVlan", join5.entity().getSpecifyVlan(), Op.EQ); + NetworksRegularUserCanCreateSearch.join("ntwkOff", join5, NetworksRegularUserCanCreateSearch.entity().getNetworkOfferingId(), join5.entity().getId(), JoinBuilder.JoinType.INNER); + NetworksRegularUserCanCreateSearch.done(); _tgMacAddress = _tgs.get("macAddress"); @@ -416,5 +433,14 @@ public class NetworkDaoImpl extends GenericDaoBase implements N List count = customSearch(sc, null); return count.get(0); } + + @Override + public long countNetworksUserCanCreate(long ownerId) { + SearchCriteria sc = NetworksRegularUserCanCreateSearch.create(); + sc.setParameters("aclType", ACLType.Account); + sc.setJoinParameters("accounts", "account", ownerId); + sc.setJoinParameters("ntwkOff", "specifyVlan", false); + return customSearch(sc, null).get(0); + } } diff --git a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 7e884679107..0112b9c8967 100755 --- a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -52,6 +52,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.NetworkDao; import com.cloud.projects.Project; import com.cloud.projects.ProjectAccount.Role; import com.cloud.projects.dao.ProjectAccountDao; @@ -117,6 +118,8 @@ public class ResourceLimitManagerImpl implements ResourceLimitService, Manager { private ProjectDao _projectDao; @Inject private ProjectAccountDao _projectAccountDao; + @Inject + private NetworkDao _networkDao; protected SearchBuilder ResourceCountSearch; ScheduledExecutorService _rcExecutor; @@ -162,12 +165,14 @@ public class ResourceLimitManagerImpl implements ResourceLimitService, Manager { projectResourceLimitMap.put(Resource.ResourceType.template, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectTemplates.key()))); projectResourceLimitMap.put(Resource.ResourceType.user_vm, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectUserVms.key()))); projectResourceLimitMap.put(Resource.ResourceType.volume, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectVolumes.key()))); + projectResourceLimitMap.put(Resource.ResourceType.network, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectNetworks.key()))); accountResourceLimitMap.put(Resource.ResourceType.public_ip, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountPublicIPs.key()))); accountResourceLimitMap.put(Resource.ResourceType.snapshot, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountSnapshots.key()))); accountResourceLimitMap.put(Resource.ResourceType.template, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountTemplates.key()))); accountResourceLimitMap.put(Resource.ResourceType.user_vm, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountUserVms.key()))); accountResourceLimitMap.put(Resource.ResourceType.volume, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountVolumes.key()))); + accountResourceLimitMap.put(Resource.ResourceType.network, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountNetworks.key()))); return true; } @@ -724,6 +729,8 @@ public class ResourceLimitManagerImpl implements ResourceLimitService, Manager { newCount = _vmTemplateDao.countTemplatesForAccount(accountId); } else if (type == Resource.ResourceType.project) { newCount = _projectAccountDao.countByAccountIdAndRole(accountId, Role.Admin); + } else if (type == Resource.ResourceType.network) { + newCount = _networkDao.countNetworksUserCanCreate(accountId); } else { throw new InvalidParameterValueException("Unsupported resource type " + type); } diff --git a/server/src/com/cloud/upgrade/dao/Upgrade218to22.java b/server/src/com/cloud/upgrade/dao/Upgrade218to22.java index c37f60b4d1b..47b7d877e7a 100644 --- a/server/src/com/cloud/upgrade/dao/Upgrade218to22.java +++ b/server/src/com/cloud/upgrade/dao/Upgrade218to22.java @@ -1477,7 +1477,7 @@ public class Upgrade218to22 implements DbUpgrade { upgradeDomainResourceCounts(conn, ResourceType.public_ip); } - private void upgradeDomainResourceCounts(Connection conn, ResourceType resourceType) { + public static void upgradeDomainResourceCounts(Connection conn, ResourceType resourceType) { try { PreparedStatement account_count_pstmt = conn.prepareStatement("SELECT account_id, count from resource_count where type='" + resourceType + "'"); diff --git a/server/src/com/cloud/upgrade/dao/Upgrade2214to30.java b/server/src/com/cloud/upgrade/dao/Upgrade2214to30.java index a0a3a83c8bb..1c198deda69 100755 --- a/server/src/com/cloud/upgrade/dao/Upgrade2214to30.java +++ b/server/src/com/cloud/upgrade/dao/Upgrade2214to30.java @@ -31,6 +31,7 @@ import java.util.UUID; import org.apache.log4j.Logger; +import com.cloud.configuration.Resource.ResourceType; import com.cloud.offering.NetworkOffering; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.exception.CloudRuntimeException; @@ -88,6 +89,10 @@ public class Upgrade2214to30 implements DbUpgrade { migrateUserConcentratedPlannerChoice(conn); // update domain router table for element it; updateRouters(conn); + // update network account resource count + udpateAccountNetworkResourceCount(conn); + // update network domain resource count + udpateDomainNetworkResourceCount(conn); } @Override @@ -1089,5 +1094,57 @@ public class Upgrade2214to30 implements DbUpgrade { } catch (SQLException e) { } } - } + } + + protected void udpateAccountNetworkResourceCount(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + ResultSet rs1 = null; + long accountId = 0; + try { + pstmt = conn.prepareStatement("select id from `cloud`.`account` where removed is null"); + rs = pstmt.executeQuery(); + while (rs.next()) { + accountId = rs.getLong(1); + + //get networks count for the account + pstmt = conn.prepareStatement("select count(*) from `cloud`.`networks` n, `cloud`.`account_network_ref` a, `cloud`.`network_offerings` no" + + " WHERE n.acl_type='Account' and n.id=a.network_id and a.account_id=? and a.is_owner=1 and no.specify_vlan=false and no.traffic_type='Guest'"); + pstmt.setLong(1, accountId); + rs1 = pstmt.executeQuery(); + long count = 0; + while (rs1.next()) { + count = rs1.getLong(1); + } + + pstmt = conn.prepareStatement("insert into `cloud`.`resource_count` (account_id, domain_id, type, count) VALUES (?, null, 'network', ?)"); + pstmt.setLong(1, accountId); + pstmt.setLong(2, count); + pstmt.executeUpdate(); + s_logger.debug("Updated network resource count for account id=" + accountId + " to be " + count); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to update network resource count for account id=" + accountId, e); + } finally { + try { + if (rs != null) { + rs.close(); + } + + if (rs1 != null) { + rs1.close(); + } + + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } + + protected void udpateDomainNetworkResourceCount(Connection conn) { + Upgrade218to22.upgradeDomainResourceCounts(conn, ResourceType.network); + } + } diff --git a/setup/db/db/schema-2214to30.sql b/setup/db/db/schema-2214to30.sql index b6954dbc7d0..292971df6e9 100755 --- a/setup/db/db/schema-2214to30.sql +++ b/setup/db/db/schema-2214to30.sql @@ -139,6 +139,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT' INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.templates', '20', 'The default maximum number of templates that can be deployed for a project'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.snapshots', '20', 'The default maximum number of snapshots that can be created for a project'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.volumes', '20', 'The default maximum number of volumes that can be created for a project'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.networks', '20', 'The default maximum number of networks that can be created for a project'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'project.invite.required', 'false', 'If invitation confirmation is required when add account to project. Default value is false'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'project.invite.timeout', '86400', 'Invitation expiration time (in seconds). Default is 1 day - 86400 seconds'); @@ -716,3 +717,5 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'manag UPDATE `cloud`.`network_offerings` SET display_text='Offering for Shared Security group enabled networks' where display_text='System-Guest-Network'; UPDATE `cloud`.`configuration` SET category = 'Secure' where name in ('alert.smtp.password', 'network.loadbalancer.haproxy.stats.auth', 'security.singlesignon.key', 'project.smtp.password'); + +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.networks', '20', 'The default maximum number of networks that can be created for an account');