Return-Path: X-Original-To: apmail-cloudstack-commits-archive@www.apache.org Delivered-To: apmail-cloudstack-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 9E533F2CD for ; Tue, 16 Apr 2013 18:03:35 +0000 (UTC) Received: (qmail 52850 invoked by uid 500); 16 Apr 2013 18:03:34 -0000 Delivered-To: apmail-cloudstack-commits-archive@cloudstack.apache.org Received: (qmail 52769 invoked by uid 500); 16 Apr 2013 18:03:34 -0000 Mailing-List: contact commits-help@cloudstack.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cloudstack.apache.org Delivered-To: mailing list commits@cloudstack.apache.org Received: (qmail 52643 invoked by uid 99); 16 Apr 2013 18:03:34 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 16 Apr 2013 18:03:34 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 1054E1F9C6; Tue, 16 Apr 2013 18:03:34 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: bfederle@apache.org To: commits@cloudstack.apache.org Date: Tue, 16 Apr 2013 18:03:35 -0000 Message-Id: <07f33b57c9c7461393494223cd3738d5@git.apache.org> In-Reply-To: <509132ed9c8b41949fc3b80bdc05905e@git.apache.org> References: <509132ed9c8b41949fc3b80bdc05905e@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [02/50] [abbrv] git commit: updated refs/heads/ui-new-project-switcher to a748988 Dedicate Public IP address range to an account Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/8f865c5a Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/8f865c5a Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/8f865c5a Branch: refs/heads/ui-new-project-switcher Commit: 8f865c5a901d1fb066709e1809fd5538af3eea11 Parents: 95cbb79 Author: Likitha Shetty Authored: Mon Apr 8 22:44:45 2013 +0530 Committer: Likitha Shetty Committed: Fri Apr 12 23:27:35 2013 +0530 ---------------------------------------------------------------------- .../cloud/configuration/ConfigurationService.java | 6 + api/src/com/cloud/event/EventTypes.java | 4 + .../admin/vlan/DedicatePublicIpRangeCmd.java | 108 ++++ .../admin/vlan/ReleasePublicIpRangeCmd.java | 77 +++ client/tomcatconf/commands.properties.in | 2 + .../cloud/configuration/ConfigurationManager.java | 5 +- .../configuration/ConfigurationManagerImpl.java | 173 ++++++- .../src/com/cloud/network/NetworkManagerImpl.java | 45 ++- .../src/com/cloud/network/NetworkServiceImpl.java | 9 - .../src/com/cloud/server/ManagementServerImpl.java | 3 + server/src/com/cloud/user/AccountManagerImpl.java | 6 +- .../configuration/ConfigurationManagerTest.java | 413 +++++++++++++++ .../cloud/vpc/MockConfigurationManagerImpl.java | 23 +- test/integration/component/test_public_ip_range.py | 173 ++++++ tools/marvin/marvin/integration/lib/base.py | 19 +- 15 files changed, 1017 insertions(+), 49 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/api/src/com/cloud/configuration/ConfigurationService.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/configuration/ConfigurationService.java b/api/src/com/cloud/configuration/ConfigurationService.java index e63fcec..6937d0b 100644 --- a/api/src/com/cloud/configuration/ConfigurationService.java +++ b/api/src/com/cloud/configuration/ConfigurationService.java @@ -35,7 +35,9 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -234,6 +236,10 @@ public interface ConfigurationService { boolean deleteVlanIpRange(DeleteVlanIpRangeCmd cmd); + Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) throws ResourceAllocationException; + + boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd); + NetworkOffering createNetworkOffering(CreateNetworkOfferingCmd cmd); NetworkOffering updateNetworkOffering(UpdateNetworkOfferingCmd cmd); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/api/src/com/cloud/event/EventTypes.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 6a26212..0ee7f40 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -226,6 +226,8 @@ public class EventTypes { // VLANs/IP ranges public static final String EVENT_VLAN_IP_RANGE_CREATE = "VLAN.IP.RANGE.CREATE"; public static final String EVENT_VLAN_IP_RANGE_DELETE = "VLAN.IP.RANGE.DELETE"; + public static final String EVENT_VLAN_IP_RANGE_DEDICATE = "VLAN.IP.RANGE.DEDICATE"; + public static final String EVENT_VLAN_IP_RANGE_RELEASE = "VLAN.IP.RANGE.RELEASE"; public static final String EVENT_STORAGE_IP_RANGE_CREATE = "STORAGE.IP.RANGE.CREATE"; public static final String EVENT_STORAGE_IP_RANGE_DELETE = "STORAGE.IP.RANGE.DELETE"; @@ -545,6 +547,8 @@ public class EventTypes { // VLANs/IP ranges entityEventDetails.put(EVENT_VLAN_IP_RANGE_CREATE, Vlan.class.getName()); entityEventDetails.put(EVENT_VLAN_IP_RANGE_DELETE,Vlan.class.getName()); + entityEventDetails.put(EVENT_VLAN_IP_RANGE_DEDICATE, Vlan.class.getName()); + entityEventDetails.put(EVENT_VLAN_IP_RANGE_RELEASE,Vlan.class.getName()); entityEventDetails.put(EVENT_STORAGE_IP_RANGE_CREATE, StorageNetworkIpRange.class.getName()); entityEventDetails.put(EVENT_STORAGE_IP_RANGE_DELETE, StorageNetworkIpRange.class.getName()); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java new file mode 100755 index 0000000..e7b1105 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java @@ -0,0 +1,108 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.vlan; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.VlanIpRangeResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import com.cloud.dc.Vlan; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + +@APICommand(name = "dedicatePublicIpRange", description="Dedicates a Public IP range to an account", responseObject=VlanIpRangeResponse.class) +public class DedicatePublicIpRangeCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DedicatePublicIpRangeCmd.class.getName()); + + private static final String s_name = "dedicatepubliciprangeresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = VlanIpRangeResponse.class, + required=true, description="the id of the VLAN IP range") + private Long id; + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, required=true, + description="account who will own the VLAN") + private String accountName; + + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.UUID, entityType = ProjectResponse.class, + description="project who will own the VLAN") + private Long projectId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType = DomainResponse.class, + required=true, description="domain ID of the account owning a VLAN") + private Long domainId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getProjectId() { + return projectId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() throws ResourceUnavailableException, ResourceAllocationException { + Vlan result = _configService.dedicatePublicIpRange(this); + if (result != null) { + VlanIpRangeResponse response = _responseGenerator.createVlanIpRangeResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate vlan ip range"); + } + } + +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java new file mode 100644 index 0000000..91cc7d3 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/vlan/ReleasePublicIpRangeCmd.java @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.vlan; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.VlanIpRangeResponse; +import org.apache.log4j.Logger; + +import com.cloud.user.Account; + +@APICommand(name = "releasePublicIpRange", description="Releases a Public IP range back to the system pool", responseObject=SuccessResponse.class) +public class ReleasePublicIpRangeCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ReleasePublicIpRangeCmd.class.getName()); + + private static final String s_name = "releasepubliciprangeresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = VlanIpRangeResponse.class, + required=true, description="the id of the Public IP range") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + boolean result = _configService.releasePublicIpRange(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release public ip range"); + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/client/tomcatconf/commands.properties.in ---------------------------------------------------------------------- diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 4ce9fd3..798d226 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -124,6 +124,8 @@ listDiskOfferings=15 createVlanIpRange=1 deleteVlanIpRange=1 listVlanIpRanges=1 +dedicatePublicIpRange=1 +releasePublicIpRange=1 #### address commands associateIpAddress=15 http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/server/src/com/cloud/configuration/ConfigurationManager.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/configuration/ConfigurationManager.java b/server/src/com/cloud/configuration/ConfigurationManager.java old mode 100644 new mode 100755 index c5f65e9..d7faf19 --- a/server/src/com/cloud/configuration/ConfigurationManager.java +++ b/server/src/com/cloud/configuration/ConfigurationManager.java @@ -30,6 +30,7 @@ import com.cloud.dc.Vlan; 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.Capability; import com.cloud.network.Network.Provider; @@ -150,6 +151,8 @@ public interface ConfigurationManager extends ConfigurationService, Manager { */ boolean deleteVlanAndPublicIpRange(long userId, long vlanDbId, Account caller); + boolean releasePublicIpRange(long userId, long vlanDbId, Account caller); + /** * Converts a comma separated list of tags to a List * @@ -211,7 +214,7 @@ public interface ConfigurationManager extends ConfigurationService, Manager { ClusterVO getCluster(long id); - boolean deleteAccountSpecificVirtualRanges(long accountId); + boolean releaseAccountSpecificVirtualRanges(long accountId); /** * Edits a pod in the database. Will not allow you to edit pods that are being used anywhere in the system. http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index ceeae1e..fce3c01 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -56,7 +56,9 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -2306,9 +2308,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Gateway, netmask and zoneId have to be passed in for virtual and direct untagged networks"); } - // if it's an account specific range, associate ip address list to the account - boolean associateIpRangeToAccount = false; - if (forVirtualNetwork) { if (vlanOwner != null) { @@ -2316,8 +2315,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati //check resource limits _resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountIpRange); - - associateIpRangeToAccount = true; } } @@ -2332,21 +2329,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati endIP, vlanGateway, vlanNetmask, vlanId, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr); txn.commit(); - if (associateIpRangeToAccount) { - _networkMgr.associateIpAddressListToAccount(userId, vlanOwner.getId(), zoneId, vlan.getId(), null); - } - - // Associate ips to the network - if (associateIpRangeToAccount) { - if (network.getState() == Network.State.Implemented) { - s_logger.debug("Applying ip associations for vlan id=" + vlanId + " in network " + network); - if (!_networkMgr.applyIpAssociations(network, false)) { - s_logger.warn("Failed to apply ip associations for vlan id=1 as a part of add vlan range for account id=" + vlanOwner.getId()); - } - } else { - s_logger.trace("Network id=" + network.getId() + " is not Implemented, no need to apply ipAssociations"); - } - } return vlan; } @@ -2699,6 +2681,149 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_DEDICATE, eventDescription = "dedicating vlan ip range", async = false) + public Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) throws ResourceAllocationException { + Long vlanDbId = cmd.getId(); + String accountName = cmd.getAccountName(); + Long domainId = cmd.getDomainId(); + Long projectId = cmd.getProjectId(); + + // Check if account is valid + Account vlanOwner = null; + if (projectId != null) { + if (accountName != null) { + throw new InvalidParameterValueException("accountName and projectId are mutually exclusive"); + } + Project project = _projectMgr.getProject(projectId); + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + projectId); + } + vlanOwner = _accountMgr.getAccount(project.getProjectAccountId()); + } + + if ((accountName != null) && (domainId != null)) { + vlanOwner = _accountDao.findActiveAccount(accountName, domainId); + if (vlanOwner == null) { + throw new InvalidParameterValueException("Please specify a valid account"); + } + } + + // Check if range is valid + VlanVO vlan = _vlanDao.findById(vlanDbId); + if (vlan == null) { + throw new InvalidParameterValueException("Please specify a valid Public IP range id"); + } + + // Check if range has already been dedicated + List maps = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanDbId); + if (maps != null && !maps.isEmpty()) { + throw new InvalidParameterValueException("Specified Public IP range has already been dedicated"); + } + + // Verify that zone exists and is advanced + Long zoneId = vlan.getDataCenterId(); + DataCenterVO zone = _zoneDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Unable to find zone by id " + zoneId); + } + if (zone.getNetworkType() == NetworkType.Basic) { + throw new InvalidParameterValueException("Public IP range can be dedicated to an account only in the zone of type " + NetworkType.Advanced); + } + + // Check Public IP resource limits + int accountPublicIpRange = _publicIpAddressDao.countIPs(zoneId, vlanDbId, false); + _resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountPublicIpRange); + + // Check if any of the Public IP addresses is allocated to another account + List ips = _publicIpAddressDao.listByVlanId(vlanDbId); + for (IPAddressVO ip : ips) { + Long allocatedToAccountId = ip.getAllocatedToAccountId(); + if (allocatedToAccountId != null) { + Account accountAllocatedTo = _accountMgr.getActiveAccountById(allocatedToAccountId); + if (!accountAllocatedTo.getAccountName().equalsIgnoreCase(accountName)) + throw new InvalidParameterValueException("Public IP address in range is already allocated to another account"); + } + } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + + // Create an AccountVlanMapVO entry + AccountVlanMapVO accountVlanMapVO = new AccountVlanMapVO(vlanOwner.getId(), vlan.getId()); + _accountVlanMapDao.persist(accountVlanMapVO); + + txn.commit(); + + return vlan; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_RELEASE, eventDescription = "releasing a public ip range", async = false) + public boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd) { + Long vlanDbId = cmd.getId(); + + VlanVO vlan = _vlanDao.findById(vlanDbId); + if (vlan == null) { + throw new InvalidParameterValueException("Please specify a valid IP range id."); + } + + return releasePublicIpRange(vlanDbId, UserContext.current().getCallerUserId(), UserContext.current().getCaller()); + } + + @Override + @DB + public boolean releasePublicIpRange(long vlanDbId, long userId, Account caller) { + VlanVO vlan = _vlanDao.findById(vlanDbId); + + List acctVln = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanDbId); + // Verify range is dedicated + if (acctVln == null || acctVln.isEmpty()) { + throw new InvalidParameterValueException("Can't release Public IP range " + vlanDbId + " as it not dedicated to any account"); + } + + // Check if range has any allocated public IPs + long allocIpCount = _publicIpAddressDao.countIPs(vlan.getDataCenterId(), vlanDbId, true); + boolean success = true; + if (allocIpCount > 0) { + try { + vlan = _vlanDao.acquireInLockTable(vlanDbId, 30); + if (vlan == null) { + throw new CloudRuntimeException("Unable to acquire vlan configuration: " + vlanDbId); + } + if (s_logger.isDebugEnabled()) { + s_logger.debug("lock vlan " + vlanDbId + " is acquired"); + } + List ips = _publicIpAddressDao.listByVlanId(vlanDbId); + for (IPAddressVO ip : ips) { + // Disassociate allocated IP's that are not in use + if ( !ip.isOneToOneNat() && !(ip.isSourceNat() && _networkModel.getNetwork(ip.getAssociatedWithNetworkId()) != null) && + !(_firewallDao.countRulesByIpId(ip.getId()) > 0) ) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Releasing Public IP addresses" + ip +" of vlan " + vlanDbId + " as part of Public IP" + + " range release to the system pool"); + } + success = success && _networkMgr.disassociatePublicIpAddress(ip.getId(), userId, caller); + } + } + if (!success) { + s_logger.warn("Some Public IP addresses that were not in use failed to be released as a part of" + + " vlan " + vlanDbId + "release to the system pool"); + } + } finally { + _vlanDao.releaseFromLockTable(vlanDbId); + } + } + + // A Public IP range can only be dedicated to one account at a time + if (_accountVlanMapDao.remove(acctVln.get(0).getId())) { + return true; + } else { + return false; + } + } + + @Override public List csvTagsToList(String tags) { List tagsList = new ArrayList(); @@ -3957,14 +4082,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB - public boolean deleteAccountSpecificVirtualRanges(long accountId) { + public boolean releaseAccountSpecificVirtualRanges(long accountId) { List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(accountId); boolean result = true; if (maps != null && !maps.isEmpty()) { Transaction txn = Transaction.currentTxn(); txn.start(); for (AccountVlanMapVO map : maps) { - if (!deleteVlanAndPublicIpRange(_accountMgr.getSystemUser().getId(), map.getVlanDbId(), + if (!releasePublicIpRange(map.getVlanDbId(), _accountMgr.getSystemUser().getId(), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM))) { result = false; } @@ -3972,10 +4097,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (result) { txn.commit(); } else { - s_logger.error("Failed to delete account specific virtual ip ranges for account id=" + accountId); + s_logger.error("Failed to release account specific virtual ip ranges for account id=" + accountId); } } else { - s_logger.trace("Account id=" + accountId + " has no account specific virtual ip ranges, nothing to delete"); + s_logger.trace("Account id=" + accountId + " has no account specific virtual ip ranges, nothing to release"); } return result; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/server/src/com/cloud/network/NetworkManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index a97f2ce..5b60466 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -348,7 +348,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } @DB - public PublicIp fetchNewPublicIp(long dcId, Long podId, Long vlanDbId, Account owner, VlanType vlanUse, + public PublicIp fetchNewPublicIp(long dcId, Long podId, List vlanDbIds, Account owner, VlanType vlanUse, Long guestNetworkId, boolean sourceNat, boolean assign, String requestedIp, boolean isSystem, Long vpcId) throws InsufficientAddressCapacityException { StringBuilder errorMessage = new StringBuilder("Unable to get ip adress in "); @@ -364,9 +364,9 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L errorMessage.append(" zone id=" + dcId); } - if (vlanDbId != null) { - sc.addAnd("vlanId", SearchCriteria.Op.EQ, vlanDbId); - errorMessage.append(", vlanId id=" + vlanDbId); + if ( vlanDbIds != null && !vlanDbIds.isEmpty() ) { + sc.setParameters("vlanId", vlanDbIds.toArray()); + errorMessage.append(", vlanId id=" + vlanDbIds.toArray()); } sc.setParameters("dc", dcId); @@ -526,14 +526,14 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } // If account has Account specific ip ranges, try to allocate ip from there - Long vlanId = null; + List vlanIds = new ArrayList(); List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(ownerId); if (maps != null && !maps.isEmpty()) { - vlanId = maps.get(0).getVlanDbId(); + vlanIds.add(maps.get(0).getVlanDbId()); } - ip = fetchNewPublicIp(dcId, null, vlanId, owner, VlanType.VirtualNetwork, guestNtwkId, + ip = fetchNewPublicIp(dcId, null, vlanIds, owner, VlanType.VirtualNetwork, guestNtwkId, isSourceNat, false, null, false, vpcId); IPAddressVO publicIp = ip.ip(); @@ -669,6 +669,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L VlanType vlanType = VlanType.VirtualNetwork; boolean assign = false; + boolean allocateFromDedicatedRange = false; if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { // zone is of type DataCenter. See DataCenterVO.java. @@ -702,8 +703,32 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L txn.start(); - ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, - false, assign, null, isSystem, null); + // If account has dedicated Public IP ranges, allocate IP from the dedicated range + List vlanDbIds = new ArrayList(); + List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(ipOwner.getId()); + for (AccountVlanMapVO map : maps) { + vlanDbIds.add(map.getVlanDbId()); + } + if (vlanDbIds != null && !vlanDbIds.isEmpty()) { + allocateFromDedicatedRange = true; + } + + try { + if (allocateFromDedicatedRange) { + ip = fetchNewPublicIp(zone.getId(), null, vlanDbIds, ipOwner, vlanType, null, + false, assign, null, isSystem, null); + } + } catch(InsufficientAddressCapacityException e) { + s_logger.warn("All IPs dedicated to account " + ipOwner.getId() + " has been acquired." + + " Now acquiring from the system pool"); + txn.close(); + allocateFromDedicatedRange = false; + } + + if (!allocateFromDedicatedRange) { + ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, false, assign, null, + isSystem, null); + } if (ip == null) { @@ -1082,7 +1107,7 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L AssignIpAddressSearch = _ipAddressDao.createSearchBuilder(); AssignIpAddressSearch.and("dc", AssignIpAddressSearch.entity().getDataCenterId(), Op.EQ); AssignIpAddressSearch.and("allocated", AssignIpAddressSearch.entity().getAllocatedTime(), Op.NULL); - AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.EQ); + AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.IN); SearchBuilder vlanSearch = _vlanDao.createSearchBuilder(); vlanSearch.and("type", vlanSearch.entity().getVlanType(), Op.EQ); vlanSearch.and("networkId", vlanSearch.entity().getNetworkId(), Op.EQ); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/server/src/com/cloud/network/NetworkServiceImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 4eb620c..a8cbaa7 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -697,15 +697,6 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { throw new IllegalArgumentException("only ip addresses that belong to a virtual network may be disassociated."); } - // Check for account wide pool. It will have an entry for account_vlan_map. - if (_accountVlanMapDao.findAccountVlanMap(ipVO.getAllocatedToAccountId(), ipVO.getVlanId()) != null) { - //see IPaddressVO.java - InvalidParameterValueException ex = new InvalidParameterValueException("Sepcified IP address uuid belongs to" + - " Account wide IP pool and cannot be disassociated"); - ex.addProxyObject("user_ip_address", ipAddressId, "ipAddressId"); - throw ex; - } - // don't allow releasing system ip address if (ipVO.getSystem()) { InvalidParameterValueException ex = new InvalidParameterValueException("Can't release system IP address with specified id"); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/server/src/com/cloud/server/ManagementServerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index b869d1e..d9a4317 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -59,6 +59,7 @@ import org.apache.cloudstack.api.command.admin.storage.*; import org.apache.cloudstack.api.command.admin.systemvm.*; import org.apache.cloudstack.api.command.admin.usage.*; import org.apache.cloudstack.api.command.admin.user.*; +import org.apache.cloudstack.api.command.admin.vlan.*; import org.apache.cloudstack.api.command.admin.vpc.*; import org.apache.cloudstack.api.command.user.autoscale.*; import org.apache.cloudstack.api.command.user.firewall.*; @@ -2037,6 +2038,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(CreateVlanIpRangeCmd.class); cmdList.add(DeleteVlanIpRangeCmd.class); cmdList.add(ListVlanIpRangesCmd.class); + cmdList.add(DedicatePublicIpRangeCmd.class); + cmdList.add(ReleasePublicIpRangeCmd.class); cmdList.add(AssignVMCmd.class); cmdList.add(MigrateVMCmd.class); cmdList.add(RecoverVMCmd.class); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/server/src/com/cloud/user/AccountManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index fe714c5..e74c491 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -690,13 +690,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M accountCleanupNeeded = true; } - // delete account specific Virtual vlans (belong to system Public Network) - only when networks are cleaned + // release account specific Virtual vlans (belong to system Public Network) - only when networks are cleaned // up successfully if (networksDeleted) { - if (!_configMgr.deleteAccountSpecificVirtualRanges(accountId)) { + if (!_configMgr.releaseAccountSpecificVirtualRanges(accountId)) { accountCleanupNeeded = true; } else { - s_logger.debug("Account specific Virtual IP ranges " + " are successfully deleted as a part of account id=" + accountId + " cleanup."); + s_logger.debug("Account specific Virtual IP ranges " + " are successfully released as a part of account id=" + accountId + " cleanup."); } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/server/test/com/cloud/configuration/ConfigurationManagerTest.java ---------------------------------------------------------------------- diff --git a/server/test/com/cloud/configuration/ConfigurationManagerTest.java b/server/test/com/cloud/configuration/ConfigurationManagerTest.java new file mode 100755 index 0000000..ee98d53 --- /dev/null +++ b/server/test/com/cloud/configuration/ConfigurationManagerTest.java @@ -0,0 +1,413 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.configuration; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.lang.reflect.Field; + +import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.cloud.configuration.Resource.ResourceType; +import com.cloud.dc.AccountVlanMapVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.Vlan; +import com.cloud.dc.VlanVO; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.dao.AccountVlanMapDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.network.NetworkManager; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.projects.ProjectManager; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.ResourceLimitService; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.net.Ip; + +import junit.framework.Assert; + +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doNothing; + +public class ConfigurationManagerTest { + + private static final Logger s_logger = Logger.getLogger(ConfigurationManagerTest.class); + + ConfigurationManagerImpl configurationMgr = new ConfigurationManagerImpl(); + + DedicatePublicIpRangeCmd dedicatePublicIpRangesCmd = new DedicatePublicIpRangeCmdExtn(); + Class _dedicatePublicIpRangeClass = dedicatePublicIpRangesCmd.getClass().getSuperclass(); + + ReleasePublicIpRangeCmd releasePublicIpRangesCmd = new ReleasePublicIpRangeCmdExtn(); + Class _releasePublicIpRangeClass = releasePublicIpRangesCmd.getClass().getSuperclass(); + + @Mock AccountManager _accountMgr; + @Mock ProjectManager _projectMgr; + @Mock ResourceLimitService _resourceLimitMgr; + @Mock NetworkManager _networkMgr; + @Mock AccountDao _accountDao; + @Mock VlanDao _vlanDao; + @Mock AccountVlanMapDao _accountVlanMapDao; + @Mock IPAddressDao _publicIpAddressDao; + @Mock DataCenterDao _zoneDao; + @Mock FirewallRulesDao _firewallDao; + + VlanVO vlan = new VlanVO(Vlan.VlanType.VirtualNetwork, "vlantag", "vlangateway","vlannetmask", 1L, "iprange", 1L, 1L, null, null, null); + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + configurationMgr._accountMgr = _accountMgr; + configurationMgr._projectMgr = _projectMgr; + configurationMgr._resourceLimitMgr = _resourceLimitMgr; + configurationMgr._networkMgr = _networkMgr; + configurationMgr._accountDao = _accountDao; + configurationMgr._vlanDao = _vlanDao; + configurationMgr._accountVlanMapDao = _accountVlanMapDao; + configurationMgr._publicIpAddressDao = _publicIpAddressDao; + configurationMgr._zoneDao = _zoneDao; + configurationMgr._firewallDao = _firewallDao; + + Account account = (Account) new AccountVO("testaccount", 1, "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(configurationMgr._accountMgr.getAccount(anyLong())).thenReturn(account); + when(configurationMgr._accountDao.findActiveAccount(anyString(), anyLong())).thenReturn(account); + when(configurationMgr._accountMgr.getActiveAccountById(anyLong())).thenReturn(account); + + when(configurationMgr._publicIpAddressDao.countIPs(anyLong(), anyLong(), anyBoolean())).thenReturn(1); + + doNothing().when(configurationMgr._resourceLimitMgr).checkResourceLimit(any(Account.class), + any(ResourceType.class), anyLong()); + + when(configurationMgr._accountVlanMapDao.persist(any(AccountVlanMapVO.class))).thenReturn(new AccountVlanMapVO()); + + when(configurationMgr._vlanDao.acquireInLockTable(anyLong(), anyInt())).thenReturn(vlan); + + UserContext.registerContext(1, account, null, true); + + Field dedicateIdField = _dedicatePublicIpRangeClass.getDeclaredField("id"); + dedicateIdField.setAccessible(true); + dedicateIdField.set(dedicatePublicIpRangesCmd, 1L); + + Field accountNameField = _dedicatePublicIpRangeClass.getDeclaredField("accountName"); + accountNameField.setAccessible(true); + accountNameField.set(dedicatePublicIpRangesCmd, "accountname"); + + Field projectIdField = _dedicatePublicIpRangeClass.getDeclaredField("projectId"); + projectIdField.setAccessible(true); + projectIdField.set(dedicatePublicIpRangesCmd, null); + + Field domainIdField = _dedicatePublicIpRangeClass.getDeclaredField("domainId"); + domainIdField.setAccessible(true); + domainIdField.set(dedicatePublicIpRangesCmd, 1L); + + Field releaseIdField = _releasePublicIpRangeClass.getDeclaredField("id"); + releaseIdField.setAccessible(true); + releaseIdField.set(releasePublicIpRangesCmd, 1L); + } + + @Test + public void testDedicatePublicIpRange() throws Exception { + + s_logger.info("Running tests for DedicatePublicIpRange API"); + + /* + * TEST 1: given valid parameters DedicatePublicIpRange should succeed + */ + runDedicatePublicIpRangePostiveTest(); + + /* + * TEST 2: given invalid public ip range DedicatePublicIpRange should fail + */ + runDedicatePublicIpRangeInvalidRange(); + /* + * TEST 3: given public IP range that is already dedicated to a different account DedicatePublicIpRange should fail + */ + runDedicatePublicIpRangeDedicatedRange(); + + /* + * TEST 4: given zone is of type Basic DedicatePublicIpRange should fail + */ + runDedicatePublicIpRangeInvalidZone(); + + /* + * TEST 5: given range is already allocated to a different account DedicatePublicIpRange should fail + */ + runDedicatePublicIpRangeIPAdressAllocated(); + } + + @Test + public void testReleasePublicIpRange() throws Exception { + + s_logger.info("Running tests for DedicatePublicIpRange API"); + + /* + * TEST 1: given valid parameters and no allocated public ip's in the range ReleasePublicIpRange should succeed + */ + runReleasePublicIpRangePostiveTest1(); + + /* + * TEST 2: given valid parameters ReleasePublicIpRange should succeed + */ + runReleasePublicIpRangePostiveTest2(); + + /* + * TEST 3: given range doesn't exist + */ + runReleasePublicIpRangeInvalidIpRange(); + + /* + * TEST 4: given range is not dedicated to any account + */ + runReleaseNonDedicatedPublicIpRange(); + } + + void runDedicatePublicIpRangePostiveTest() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangePostiveTest"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByAccount(anyLong())).thenReturn(null); + + DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", + null, null, NetworkType.Advanced, null, null, true, true, null, null); + when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); + + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + try { + Vlan result = configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + Assert.assertNotNull(result); + } catch (Exception e) { + s_logger.info("exception in testing runDedicatePublicIpRangePostiveTest message: " + e.toString()); + } finally { + txn.close("runDedicatePublicIpRangePostiveTest"); + } + } + + void runDedicatePublicIpRangeInvalidRange() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangeInvalidRange"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(null); + try { + configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Please specify a valid Public IP range id")); + } finally { + txn.close("runDedicatePublicIpRangeInvalidRange"); + } + } + + void runDedicatePublicIpRangeDedicatedRange() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangeDedicatedRange"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + // public ip range is already dedicated + List accountVlanMaps = new ArrayList(); + AccountVlanMapVO accountVlanMap = new AccountVlanMapVO(1, 1); + accountVlanMaps.add(accountVlanMap); + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(accountVlanMaps); + + DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", + null, null, NetworkType.Advanced, null, null, true, true, null, null); + when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); + + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + try { + configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Public IP range has already been dedicated")); + } finally { + txn.close("runDedicatePublicIpRangePublicIpRangeDedicated"); + } + } + + void runDedicatePublicIpRangeInvalidZone() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangeInvalidZone"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(null); + + // public ip range belongs to zone of type basic + DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", + null, null, NetworkType.Basic, null, null, true, true, null, null); + when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); + + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + try { + configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Public IP range can be dedicated to an account only in the zone of type Advanced")); + } finally { + txn.close("runDedicatePublicIpRangeInvalidZone"); + } + } + + void runDedicatePublicIpRangeIPAdressAllocated() throws Exception { + Transaction txn = Transaction.open("runDedicatePublicIpRangeIPAdressAllocated"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByAccount(anyLong())).thenReturn(null); + + DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, "10.0.0.1/24", + null, null, NetworkType.Advanced, null, null, true, true, null, null); + when(configurationMgr._zoneDao.findById(anyLong())).thenReturn(dc); + + // one of the ip addresses of the range is allocated to different account + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddress.setAllocatedToAccountId(1L); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + try { + configurationMgr.dedicatePublicIpRange(dedicatePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Public IP address in range is already allocated to another account")); + } finally { + txn.close("runDedicatePublicIpRangeIPAdressAllocated"); + } + } + + void runReleasePublicIpRangePostiveTest1() throws Exception { + Transaction txn = Transaction.open("runReleasePublicIpRangePostiveTest1"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + List accountVlanMaps = new ArrayList(); + AccountVlanMapVO accountVlanMap = new AccountVlanMapVO(1, 1); + accountVlanMaps.add(accountVlanMap); + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(accountVlanMaps); + + // no allocated ip's + when(configurationMgr._publicIpAddressDao.countIPs(anyLong(), anyLong(), anyBoolean())).thenReturn(0); + + when(configurationMgr._accountVlanMapDao.remove(anyLong())).thenReturn(true); + try { + Boolean result = configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); + Assert.assertTrue(result); + } catch (Exception e) { + s_logger.info("exception in testing runReleasePublicIpRangePostiveTest1 message: " + e.toString()); + } finally { + txn.close("runReleasePublicIpRangePostiveTest1"); + } + } + + void runReleasePublicIpRangePostiveTest2() throws Exception { + Transaction txn = Transaction.open("runReleasePublicIpRangePostiveTest2"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + List accountVlanMaps = new ArrayList(); + AccountVlanMapVO accountVlanMap = new AccountVlanMapVO(1, 1); + accountVlanMaps.add(accountVlanMap); + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(accountVlanMaps); + + when(configurationMgr._publicIpAddressDao.countIPs(anyLong(), anyLong(), anyBoolean())).thenReturn(1); + + List ipAddressList = new ArrayList(); + IPAddressVO ipAddress = new IPAddressVO(new Ip("75.75.75.75"), 1, 0xaabbccddeeffL, 10, false); + ipAddressList.add(ipAddress); + when(configurationMgr._publicIpAddressDao.listByVlanId(anyLong())).thenReturn(ipAddressList); + + when(configurationMgr._firewallDao.countRulesByIpId(anyLong())).thenReturn(0L); + + when(configurationMgr._networkMgr.disassociatePublicIpAddress(anyLong(), anyLong(), any(Account.class))).thenReturn(true); + + when(configurationMgr._vlanDao.releaseFromLockTable(anyLong())).thenReturn(true); + + when(configurationMgr._accountVlanMapDao.remove(anyLong())).thenReturn(true); + try { + Boolean result = configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); + Assert.assertTrue(result); + } catch (Exception e) { + s_logger.info("exception in testing runReleasePublicIpRangePostiveTest2 message: " + e.toString()); + } finally { + txn.close("runReleasePublicIpRangePostiveTest2"); + } + } + + void runReleasePublicIpRangeInvalidIpRange() throws Exception { + Transaction txn = Transaction.open("runReleasePublicIpRangeInvalidIpRange"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(null); + try { + configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Please specify a valid IP range id")); + } finally { + txn.close("runReleasePublicIpRangeInvalidIpRange"); + } + } + + void runReleaseNonDedicatedPublicIpRange() throws Exception { + Transaction txn = Transaction.open("runReleaseNonDedicatedPublicIpRange"); + + when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); + + when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(null); + try { + configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("as it not dedicated to any account")); + } finally { + txn.close("runReleaseNonDedicatedPublicIpRange"); + } + } + + + public class DedicatePublicIpRangeCmdExtn extends DedicatePublicIpRangeCmd { + public long getEntityOwnerId() { + return 1; + } + } + + public class ReleasePublicIpRangeCmdExtn extends ReleasePublicIpRangeCmd { + public long getEntityOwnerId() { + return 1; + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java old mode 100644 new mode 100755 index b0063fa..a03e361 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -40,7 +40,9 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -544,7 +546,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu * @see com.cloud.configuration.ConfigurationManager#deleteAccountSpecificVirtualRanges(long) */ @Override - public boolean deleteAccountSpecificVirtualRanges(long accountId) { + public boolean releaseAccountSpecificVirtualRanges(long accountId) { // TODO Auto-generated method stub return false; } @@ -613,5 +615,24 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu return null; } + @Override + public Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) + throws ResourceAllocationException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean releasePublicIpRange(long userId, long vlanDbId, + Account caller) { + // TODO Auto-generated method stub + return false; + } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/test/integration/component/test_public_ip_range.py ---------------------------------------------------------------------- diff --git a/test/integration/component/test_public_ip_range.py b/test/integration/component/test_public_ip_range.py new file mode 100755 index 0000000..f2c967f --- /dev/null +++ b/test/integration/component/test_public_ip_range.py @@ -0,0 +1,173 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +""" P1 tests for Dedicating Public IP addresses +""" +#Import Local Modules +import marvin +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +import datetime + +class Services: + """Test Dedicating Public IP addresses + """ + + def __init__(self): + self.services = { + "domain": { + "name": "Domain", + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + "password": "password", + }, + "gateway": "10.102.197.1", + "netmask": "255.255.255.0", + "forvirtualnetwork": "true", + "startip": "10.102.197.70", + "endip": "10.102.197.73", + "zoneid": "1", + "podid": "", + "vlan": "101", + } + +class TesDedicatePublicIPRange(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TesDedicatePublicIPRange, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + # Create Account + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls._cleanup = [ + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + # Clean up + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags = ["publiciprange", "dedicate", "release"]) + def test_dedicatePublicIpRange(self): + """Test public IP range dedication + """ + + # Validate the following: + # 1. Create a Public IP range + # 2. Created IP range should be present, verify with listVlanIpRanges + # 3. Dedicate the created IP range to user account + # 4. Verify IP range is dedicated, verify with listVlanIpRanges + # 5. Release the dedicated Public IP range back to the system + # 6. Verify IP range has been released, verify with listVlanIpRanges + # 7. Delete the Public IP range + + self.debug("Creating Public IP range") + self.public_ip_range = PublicIpRange.create( + self.api_client, + self.services + ) + list_public_ip_range_response = PublicIpRange.list( + self.apiclient, + id=self.public_ip_range.vlan.id + ) + self.debug( + "Verify listPublicIpRanges response for public ip ranges: %s" \ + % self.public_ip_range.vlan.id + ) + self.assertEqual( + isinstance(list_public_ip_range_response, list), + True, + "Check for list Public IP range response" + ) + public_ip_response = list_public_ip_range_response[0] + self.assertEqual( + public_ip_response.id, + self.public_ip_range.vlan.id, + "Check public ip range response id is in listVlanIpRanges" + ) + + self.debug("Dedicating Public IP range"); + dedicate_public_ip_range_response = PublicIpRange.dedicate( + self.apiclient, + self.public_ip_range.vlan.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + list_public_ip_range_response = PublicIpRange.list( + self.apiclient, + id=self.public_ip_range.vlan.id + ) + public_ip_response = list_public_ip_range_response[0] + self.assertEqual( + public_ip_response.account, + self.account.account.name, + "Check account name is in listVlanIpRanges as the account public ip range is dedicated to" + ) + + self.debug("Releasing Public IP range"); + self.public_ip_range.release(self.apiclient) + list_public_ip_range_response = PublicIpRange.list( + self.apiclient, + id=self.public_ip_range.vlan.id + ) + public_ip_response = list_public_ip_range_response[0] + self.assertEqual( + public_ip_response.account, + "system", + "Check account name is system account in listVlanIpRanges" + ) + + self.debug("Deleting Public IP range"); + self.public_ip_range.delete(self.apiclient) + + return + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f865c5a/tools/marvin/marvin/integration/lib/base.py ---------------------------------------------------------------------- diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py old mode 100644 new mode 100755 index d27ab3b..3df68ab --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -1873,7 +1873,7 @@ class PublicIpRange: """Delete VlanIpRange""" cmd = deleteVlanIpRange.deleteVlanIpRangeCmd() - cmd.id = self.id + cmd.id = self.vlan.id apiclient.deleteVlanIpRange(cmd) @classmethod @@ -1884,6 +1884,23 @@ class PublicIpRange: [setattr(cmd, k, v) for k, v in kwargs.items()] return(apiclient.listVlanIpRanges(cmd)) + @classmethod + def dedicate(cls, apiclient, id, account=None, domainid=None, projectid=None): + """Dedicate VLAN IP range""" + + cmd = dedicatePublicIpRange.dedicatePublicIpRangeCmd() + cmd.id = id + cmd.account = account + cmd.domainid = domainid + cmd.projectid = projectid + return PublicIpRange(apiclient.dedicatePublicIpRange(cmd).__dict__) + + def release(self, apiclient): + """Release VLAN IP range""" + + cmd = releasePublicIpRange.releasePublicIpRangeCmd() + cmd.id = self.vlan.id + return apiclient.releasePublicIpRange(cmd) class SecondaryStorage: """Manage Secondary storage"""