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 EC23DDD7A for ; Mon, 20 May 2013 16:36:38 +0000 (UTC) Received: (qmail 12653 invoked by uid 500); 20 May 2013 16:26:39 -0000 Delivered-To: apmail-cloudstack-commits-archive@cloudstack.apache.org Received: (qmail 68336 invoked by uid 500); 20 May 2013 16:23:11 -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 37463 invoked by uid 99); 20 May 2013 14:38:25 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 20 May 2013 14:38:25 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 3EDAD8901CB; Mon, 20 May 2013 14:38:25 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: muralireddy@apache.org To: commits@cloudstack.apache.org Date: Mon, 20 May 2013 14:38:26 -0000 Message-Id: In-Reply-To: <1d26995f364147f8963632911867fac0@git.apache.org> References: <1d26995f364147f8963632911867fac0@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [2/2] git commit: updated refs/heads/master to d6452be CLOUDSTACK-652: meging 'portable public ip' feature Squashed commit of the following: commit f244f9ce7982db16984dd87c31545f1c0240c704 Merge: 993cbb0 f5c8e38 Author: Murali Reddy Date: Mon May 20 18:54:05 2013 +0530 Merge branch 'master' into portablepublicip Conflicts: server/src/com/cloud/server/ManagementServerImpl.java server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java commit 993cbb0df9fa6e64b96b18ed775b73cdf4a8f5d7 Author: Murali Reddy Date: Mon May 20 18:49:54 2013 +0530 introduce 'transferPortableIP' interface method in network manger. This method will transfer association of portable ip from one network to another network. commit 0c1c2652c1b39e9a81ca35464360e11ed9ef23f1 Merge: a718d35 a29e393 Author: Murali Reddy Date: Fri May 17 02:48:54 2013 +0530 Merge branch 'master' into portablepublicip Conflicts: utils/src/com/cloud/utils/net/NetUtils.java commit a718d353f7acf0328d928673df6f22de1abc0acb Merge: ecca117 c211818 Author: Murali Reddy Date: Mon May 13 21:22:19 2013 +0530 Merge branch 'master' into portablepublicip Conflicts: api/src/org/apache/cloudstack/api/ResponseGenerator.java server/src/com/cloud/api/ApiResponseHelper.java server/src/com/cloud/network/NetworkServiceImpl.java server/src/com/cloud/network/addr/PublicIp.java server/src/com/cloud/server/ManagementServerImpl.java server/test/com/cloud/network/MockNetworkManagerImpl.java server/test/com/cloud/vpc/MockConfigurationManagerImpl.java server/test/com/cloud/vpc/MockNetworkManagerImpl.java setup/db/db/schema-410to420.sql commit ecca117e345224059297f5c1ffa0f442209b3160 Author: Murali Reddy Date: Mon May 13 20:05:29 2013 +0530 added integration tests for testing portable ip ranges commit 895a27c2771dbb497ecc6fe0d212589f012a48d8 Author: Murali Reddy Date: Mon May 13 15:12:19 2013 +0530 - establish model for transferring portable IP association from a network with which it is associated to another network. - enabling static nat api, extended to transfer potrtable IP across the networks if the VM/network is different from the current associate network of the portable ip commit 51509751b290c0e51cbdd104a9aebff189cbe806 Author: Murali Reddy Date: Mon May 13 12:05:33 2013 +0530 seperate out associate/disassociate with guest network operations from alloc and release of portable ip commit bd058f58c2d8d36ec25e31ed9de4cd414e0ca051 Author: Murali Reddy Date: Sun May 12 21:14:48 2013 +0530 enhance disasociateIPAddr API to release protable IP associated with a guest network or VPC commit 27504d9098729e8c3ac3b33f053f2d66ac2c4401 Author: Murali Reddy Date: Sun May 12 16:53:45 2013 +0530 enhance asociateIPAddr API to acquire a protable IP and associate with a guest network or VPC commit f82c6a8431647114462665c1306c6215cb92afd3 Merge: 3dbfb44 0749013 Author: Murali Reddy Date: Sat May 11 23:32:13 2013 +0530 Merge branch 'master' into portablepublicip Conflicts: api/src/com/cloud/network/IpAddress.java api/src/org/apache/cloudstack/api/ResponseGenerator.java client/tomcatconf/commands.properties.in server/src/com/cloud/api/ApiResponseHelper.java server/src/com/cloud/configuration/ConfigurationManagerImpl.java server/src/com/cloud/server/ManagementServerImpl.java server/test/org/apache/cloudstack/affinity/AffinityApiTestConfiguration.java server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java setup/db/db/schema-410to420.sql commit 3dbfb44eb5b888367375a96b8ae0ac9cf54309a6 Author: Murali Reddy Date: Sat May 11 20:33:19 2013 +0530 - add 'portable' boolean as property of IpAddress, persist the property in IPAddressVO, return the property in IpAddressResponse - add ability to request portable IP in associateIpAddress api commit bf3cb274cfeb1ef41c63794ced83c7c6940f34cc Author: Murali Reddy Date: Sat May 11 16:08:40 2013 +0530 add the status of each portable IP (its state, details of associated data center/VPC/guest network etc) in the PortableIpRangeResponse returned by listPortableIpRanges API commit e7b2fb22557cb4ef0ce9c8dde3ed1b9c857038bf Author: Murali Reddy Date: Sat May 11 14:36:01 2013 +0530 Introdcues notion of 'portable IP' pool at region level. Introduces root admin only API's to provision portable ip to a region - createPortableIpRange - deletePortableIpRange - listPortableIpRanges Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/d6452be8 Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/d6452be8 Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/d6452be8 Branch: refs/heads/master Commit: d6452be8618198454c6f20f4210f6ac18ce9b11d Parents: f5c8e38 Author: Murali Reddy Authored: Mon May 20 20:05:47 2013 +0530 Committer: Murali Reddy Committed: Mon May 20 20:05:47 2013 +0530 ---------------------------------------------------------------------- api/src/com/cloud/async/AsyncJob.java | 1 + .../cloud/configuration/ConfigurationService.java | 13 + api/src/com/cloud/event/EventTypes.java | 5 + api/src/com/cloud/network/IpAddress.java | 4 +- api/src/com/cloud/network/NetworkService.java | 5 + .../org/apache/cloudstack/api/ApiConstants.java | 2 + .../apache/cloudstack/api/ResponseGenerator.java | 9 +- .../admin/region/CreatePortableIpRangeCmd.java | 156 +++++++++ .../admin/region/DeletePortableIpRangeCmd.java | 93 +++++ .../admin/region/ListPortableIpRangesCmd.java | 109 ++++++ .../command/user/address/AssociateIPAddrCmd.java | 28 ++- .../user/address/DisassociateIPAddrCmd.java | 12 +- .../api/command/user/nat/EnableStaticNatCmd.java | 6 + .../cloudstack/api/response/IPAddressResponse.java | 7 + .../api/response/PortableIpRangeResponse.java | 93 +++++ .../api/response/PortableIpResponse.java | 106 ++++++ .../org/apache/cloudstack/region/PortableIp.java | 58 ++++ .../apache/cloudstack/region/PortableIpRange.java | 38 ++ client/tomcatconf/applicationContext.xml.in | 2 + client/tomcatconf/commands.properties.in | 6 + .../src/com/cloud/network/dao/IPAddressVO.java | 24 ++ server/src/com/cloud/api/ApiResponseHelper.java | 70 ++++- .../configuration/ConfigurationManagerImpl.java | 165 +++++++++- server/src/com/cloud/network/NetworkManager.java | 14 + .../src/com/cloud/network/NetworkManagerImpl.java | 262 ++++++++++++++- .../src/com/cloud/network/NetworkServiceImpl.java | 105 ++++-- server/src/com/cloud/network/addr/PublicIp.java | 9 + .../com/cloud/network/rules/RulesManagerImpl.java | 43 +++ .../src/com/cloud/server/ManagementServerImpl.java | 86 +++++- .../apache/cloudstack/region/PortableIpDao.java | 39 +++ .../cloudstack/region/PortableIpDaoImpl.java | 131 +++++++ .../cloudstack/region/PortableIpRangeDao.java | 30 ++ .../cloudstack/region/PortableIpRangeDaoImpl.java | 65 ++++ .../cloudstack/region/PortableIpRangeVO.java | 119 +++++++ .../org/apache/cloudstack/region/PortableIpVO.java | 222 ++++++++++++ .../com/cloud/network/MockNetworkManagerImpl.java | 40 ++- .../cloud/vpc/MockConfigurationManagerImpl.java | 58 ++++ .../test/com/cloud/vpc/MockNetworkManagerImpl.java | 41 ++- .../networkoffering/ChildTestConfiguration.java | 7 + setup/db/db/schema-410to420.sql | 37 ++ test/integration/smoke/test_portable_publicip.py | 237 +++++++++++++ tools/marvin/marvin/integration/lib/base.py | 36 ++ utils/src/com/cloud/utils/net/NetUtils.java | 13 + 43 files changed, 2542 insertions(+), 64 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/com/cloud/async/AsyncJob.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/async/AsyncJob.java b/api/src/com/cloud/async/AsyncJob.java index ccdc406..47f9b57 100644 --- a/api/src/com/cloud/async/AsyncJob.java +++ b/api/src/com/cloud/async/AsyncJob.java @@ -35,6 +35,7 @@ public interface AsyncJob extends Identity, InternalIdentity { Host, StoragePool, IpAddress, + PortableIpAddress, SecurityGroup, PhysicalNetwork, TrafficType, http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/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 fdbd9d6..381fcad 100644 --- a/api/src/com/cloud/configuration/ConfigurationService.java +++ b/api/src/com/cloud/configuration/ConfigurationService.java @@ -39,6 +39,9 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd; 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.region.CreatePortableIpRangeCmd; +import org.apache.cloudstack.api.command.admin.region.DeletePortableIpRangeCmd; +import org.apache.cloudstack.api.command.admin.region.ListPortableIpRangesCmd; 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; @@ -56,6 +59,8 @@ import com.cloud.offering.DiskOffering; import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; import com.cloud.user.Account; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpRange; public interface ConfigurationService { @@ -278,4 +283,12 @@ public interface ConfigurationService { * @return */ boolean isOfferingForVpc(NetworkOffering offering); + + PortableIpRange createPortableIpRange(CreatePortableIpRangeCmd cmd) throws ConcurrentOperationException; + + boolean deletePortableIpRange(DeletePortableIpRangeCmd cmd); + + List listPortableIpRanges(ListPortableIpRangesCmd cmd); + + List listPortableIps(long id); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/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 9c83f13..fcac8e8 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -102,6 +102,8 @@ public class EventTypes { // Network Events public static final String EVENT_NET_IP_ASSIGN = "NET.IPASSIGN"; public static final String EVENT_NET_IP_RELEASE = "NET.IPRELEASE"; + public static final String EVENT_PORTABLE_IP_ASSIGN = "PORTABLE.IPASSIGN"; + public static final String EVENT_PORTABLE_IP_RELEASE = "PORTABLEIPRELEASE"; public static final String EVENT_NET_RULE_ADD = "NET.RULEADD"; public static final String EVENT_NET_RULE_DELETE = "NET.RULEDELETE"; public static final String EVENT_NET_RULE_MODIFY = "NET.RULEMODIFY"; @@ -432,6 +434,9 @@ public class EventTypes { public static final String EVENT_DEDICATED_GUEST_VLAN_RANGE_RELEASE = "GUESTVLANRANGE.RELEASE"; + public static final String EVENT_PORTABLE_IP_RANGE_CREATE = "PORTABLE.IP.RANGE.CREATE"; + public static final String EVENT_PORTABLE_IP_RANGE_DELETE = "PORTABLE.IP.RANGE.DELETE"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/com/cloud/network/IpAddress.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/network/IpAddress.java b/api/src/com/cloud/network/IpAddress.java index c48e8b9..de11a6d 100644 --- a/api/src/com/cloud/network/IpAddress.java +++ b/api/src/com/cloud/network/IpAddress.java @@ -81,7 +81,9 @@ public interface IpAddress extends ControlledEntity, Identity, InternalIdentity Long getVpcId(); String getVmIp(); - + + boolean isPortable(); + Long getNetworkId(); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/com/cloud/network/NetworkService.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index 2e50c53..59702a2 100755 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -52,6 +52,11 @@ public interface NetworkService { boolean releaseIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; + IpAddress allocatePortableIP(Account ipOwner, int regionId, Long zoneId, Long networkId, Long vpcId) throws ResourceAllocationException, + InsufficientAddressCapacityException, ConcurrentOperationException; + + boolean releasePortableIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; + Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/ApiConstants.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index cf093bf..1e9435f 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -114,6 +114,7 @@ public class ApiConstants { public static final String IS_CLEANUP_REQUIRED = "iscleanuprequired"; public static final String IS_EXTRACTABLE = "isextractable"; public static final String IS_FEATURED = "isfeatured"; + public static final String IS_PORTABLE = "isportable"; public static final String IS_PUBLIC = "ispublic"; public static final String IS_PERSISTENT = "ispersistent"; public static final String IS_READY = "isready"; @@ -158,6 +159,7 @@ public class ApiConstants { public static final String POLICY_ID = "policyid"; public static final String PORT = "port"; public static final String PORTAL = "portal"; + public static final String PORTABLE_IP_ADDRESS = "portableipaddress"; public static final String PORT_FORWARDING_SERVICE_ID = "portforwardingserviceid"; public static final String PRIVATE_INTERFACE = "privateinterface"; public static final String PRIVATE_IP = "privateip"; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/ResponseGenerator.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index 10bf305..0732e77 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -119,6 +119,8 @@ import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.api.response.*; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.Region; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpRange; import org.apache.cloudstack.usage.Usage; import com.cloud.async.AsyncJob; @@ -441,7 +443,12 @@ public interface ResponseGenerator { Long getAffinityGroupId(String name, long entityOwnerId); + PortableIpRangeResponse createPortableIPRangeResponse(PortableIpRange range); + + PortableIpResponse createPortableIPResponse(PortableIp portableIp); + InternalLoadBalancerElementResponse createInternalLbElementResponse(VirtualRouterProvider result); - + IsolationMethodResponse createIsolationMethodResponse(IsolationType method); + } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/command/admin/region/CreatePortableIpRangeCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/admin/region/CreatePortableIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/region/CreatePortableIpRangeCmd.java new file mode 100644 index 0000000..78e4c94 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/region/CreatePortableIpRangeCmd.java @@ -0,0 +1,156 @@ +// 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.region; + +import javax.inject.Inject; + +import com.cloud.async.AsyncJob; +import com.cloud.dc.Vlan; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PortableIpRangeResponse; +import org.apache.cloudstack.api.response.RegionResponse; +import org.apache.cloudstack.api.response.VlanIpRangeResponse; +import org.apache.cloudstack.region.PortableIpRange; +import org.apache.cloudstack.region.Region; +import org.apache.cloudstack.region.RegionService; +import org.apache.log4j.Logger; + +import com.cloud.user.Account; + +@APICommand(name = "createPortableIpRange", responseObject=PortableIpRangeResponse.class, description="adds a range of portable public IP's to a region", since="4.2.0") +public class CreatePortableIpRangeCmd extends BaseAsyncCreateCmd { + + public static final Logger s_logger = Logger.getLogger(CreatePortableIpRangeCmd.class.getName()); + + private static final String s_name = "createportableiprangeresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.REGION_ID, type=CommandType.INTEGER, entityType = RegionResponse.class, required=true, description="Id of the Region") + private Integer regionId; + + @Parameter(name=ApiConstants.START_IP, type=CommandType.STRING, required=true, description="the beginning IP address in the portable IP range") + private String startIp; + + @Parameter(name=ApiConstants.END_IP, type=CommandType.STRING, required=true, description="the ending IP address in the portable IP range") + private String endIp; + + @Parameter(name=ApiConstants.GATEWAY, type=CommandType.STRING, required=true, description="the gateway for the portable IP range") + private String gateway; + + @Parameter(name=ApiConstants.NETMASK, type=CommandType.STRING, required=true, description="the netmask of the portable IP range") + private String netmask; + + @Parameter(name=ApiConstants.VLAN, type=CommandType.STRING, description="VLAN id, if not specified defaulted to untagged") + private String vlan; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Integer getRegionId() { + return regionId; + } + + public String getStartIp() { + return startIp; + } + + public String getEndIp() { + return endIp; + } + + public String getVlan() { + return vlan; + } + + public String getGateway() { + return gateway; + } + + public String getNetmask() { + return netmask; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + PortableIpRange portableIpRange = _entityMgr.findById(PortableIpRange.class, getEntityId()); + PortableIpRangeResponse response = null; + if (portableIpRange != null) { + response = _responseGenerator.createPortableIPRangeResponse(portableIpRange); + } + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public void create() throws ResourceAllocationException { + try { + PortableIpRange portableIpRange = _configService.createPortableIpRange(this); + if (portableIpRange != null) { + this.setEntityId(portableIpRange.getId()); + this.setEntityUuid(portableIpRange.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create portable public IP range"); + } + } catch (ConcurrentOperationException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_PORTABLE_IP_RANGE_CREATE; + } + + @Override + public String getEventDescription() { + return "creating a portable public ip range in region: " + getRegionId(); + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.PortableIpAddress; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java new file mode 100644 index 0000000..856e8ef --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/region/DeletePortableIpRangeCmd.java @@ -0,0 +1,93 @@ +// 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.region; + +import javax.inject.Inject; + +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.response.PortableIpRangeResponse; +import org.apache.cloudstack.api.response.RegionResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.region.Region; +import org.apache.cloudstack.region.RegionService; +import org.apache.log4j.Logger; + +import com.cloud.user.Account; + +@APICommand(name = "deletePortableIpRange", description="deletes a range of portable public IP's associated with a region", responseObject=SuccessResponse.class) +public class DeletePortableIpRangeCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeletePortableIpRangeCmd.class.getName()); + + private static final String s_name = "deleteportablepublicipresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, required=true, entityType = PortableIpRangeResponse.class, description="Id of the portable 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.deletePortableIpRange(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete portable ip range"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_PORTABLE_IP_RANGE_DELETE; + } + + @Override + public String getEventDescription() { + return "deleting a portable public ip range"; + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.PortableIpAddress; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/command/admin/region/ListPortableIpRangesCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/admin/region/ListPortableIpRangesCmd.java b/api/src/org/apache/cloudstack/api/command/admin/region/ListPortableIpRangesCmd.java new file mode 100644 index 0000000..75bcce0 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/region/ListPortableIpRangesCmd.java @@ -0,0 +1,109 @@ +// 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.region; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PortableIpRangeResponse; +import org.apache.cloudstack.api.response.PortableIpResponse; +import org.apache.cloudstack.api.response.RegionResponse; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpRange; +import org.apache.cloudstack.region.Region; +import org.apache.cloudstack.region.RegionService; +import org.apache.log4j.Logger; + +import com.cloud.user.Account; + +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listPortableIpRanges", description="list portable IP ranges", responseObject=PortableIpRangeResponse.class) +public class ListPortableIpRangesCmd extends BaseListCmd { + + public static final Logger s_logger = Logger.getLogger(ListPortableIpRangesCmd.class.getName()); + + private static final String s_name = "listportableipresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.REGION_ID, type=CommandType.INTEGER, required=false, description="Id of a Region") + private Integer regionId; + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, required=false, entityType = PortableIpRangeResponse.class, description="Id of the portable ip range") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Integer getRegionIdId() { + return regionId; + } + + public Long getPortableIpRangeId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + ListResponse response = new ListResponse(); + List responses = new ArrayList(); + List portableIpRanges = new ArrayList(); + + portableIpRanges = _configService.listPortableIpRanges(this); + if (portableIpRanges != null && !portableIpRanges.isEmpty()) { + for (PortableIpRange range : portableIpRanges) { + PortableIpRangeResponse rangeResponse = _responseGenerator.createPortableIPRangeResponse(range); + + List portableIps = _configService.listPortableIps(range.getId()); + if (portableIps != null && !portableIps.isEmpty()) { + List portableIpResponses = new ArrayList(); + for (PortableIp portableIP: portableIps) { + PortableIpResponse portableIpresponse = _responseGenerator.createPortableIPResponse(portableIP); + portableIpResponses.add(portableIpresponse); + } + rangeResponse.setPortableIpResponses(portableIpResponses); + } + + rangeResponse.setObjectName("portableiprange"); + responses.add(rangeResponse); + } + } + response.setResponses(responses, portableIpRanges.size()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java b/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java index 28fbae4..f37e820 100644 --- a/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java @@ -66,6 +66,14 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd { "be associated with") private Long vpcId; + @Parameter(name=ApiConstants.IS_PORTABLE, type = BaseCmd.CommandType.BOOLEAN, description = "should be set to true " + + "if public IP is required to be transferable across zones, if not specified defaults to false") + private Boolean isPortable; + + @Parameter(name=ApiConstants.REGION_ID, type=CommandType.INTEGER, entityType = RegionResponse.class, + required=false, description="region ID from where portable ip is to be associated.") + private Integer regionId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -107,6 +115,18 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd { return vpcId; } + public boolean isPortable() { + if (isPortable == null) { + return false; + } else { + return isPortable; + } + } + + public Integer getRegionId() { + return regionId; + } + public Long getNetworkId() { if (vpcId != null) { return null; @@ -196,7 +216,13 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException{ try { - IpAddress ip = _networkService.allocateIP(_accountService.getAccount(getEntityOwnerId()), getZoneId(), getNetworkId()); + IpAddress ip = null; + + if (!isPortable()) { + ip = _networkService.allocateIP(_accountService.getAccount(getEntityOwnerId()), getZoneId(), getNetworkId()); + } else { + ip = _networkService.allocatePortableIP(_accountService.getAccount(getEntityOwnerId()), 1, getZoneId(), getNetworkId(), getVpcId()); + } if (ip != null) { this.setEntityId(ip.getId()); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java b/api/src/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java index 8271119..8f78fe3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/address/DisassociateIPAddrCmd.java @@ -74,7 +74,12 @@ public class DisassociateIPAddrCmd extends BaseAsyncCmd { @Override public void execute() throws InsufficientAddressCapacityException{ UserContext.current().setEventDetails("Ip Id: " + getIpAddressId()); - boolean result = _networkService.releaseIpAddress(getIpAddressId()); + boolean result = false; + if (!isPortable(id)) { + _networkService.releaseIpAddress(getIpAddressId()); + } else { + _networkService.releaseIpAddress(getIpAddressId()); + } if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); @@ -139,4 +144,9 @@ public class DisassociateIPAddrCmd extends BaseAsyncCmd { public Long getInstanceId() { return getIpAddressId(); } + + private boolean isPortable(long id) { + IpAddress ip = getIpAddress(id); + return ip.isPortable(); + } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java b/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java index 902dbae..2e2e150 100644 --- a/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java @@ -91,6 +91,12 @@ public class EnableStaticNatCmd extends BaseCmd{ } else { ntwkId = networkId; } + + // in case of portable public IP, network ID passed takes precedence + if (ip.isPortable() && networkId != null ) { + ntwkId = networkId; + } + if (ntwkId == null) { throw new InvalidParameterValueException("Unable to enable static nat for the ipAddress id=" + ipAddressId + " as ip is not associated with any network and no networkId is passed in"); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java b/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java index cede84f..e3d5cc6 100644 --- a/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java +++ b/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java @@ -115,6 +115,9 @@ public class IPAddressResponse extends BaseResponse implements ControlledEntityR @SerializedName(ApiConstants.TAGS) @Param(description="the list of resource tags associated with ip address", responseObject = ResourceTagResponse.class) private List tags; + @SerializedName(ApiConstants.IS_PORTABLE) @Param(description = "is public IP portable across the zones") + private Boolean isPortable; + /* @SerializedName(ApiConstants.JOB_ID) @Param(description="shows the current pending asynchronous job ID. This tag is not returned if no current pending jobs are acting on the volume") private IdentityProxy jobId = new IdentityProxy("async_job"); @@ -247,4 +250,8 @@ public class IPAddressResponse extends BaseResponse implements ControlledEntityR public void setAssociatedNetworkName(String associatedNetworkName) { this.associatedNetworkName = associatedNetworkName; } + + public void setPortable(Boolean portable) { + this.isPortable = portable; + } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/response/PortableIpRangeResponse.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/response/PortableIpRangeResponse.java b/api/src/org/apache/cloudstack/api/response/PortableIpRangeResponse.java new file mode 100644 index 0000000..e7a15b8 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/PortableIpRangeResponse.java @@ -0,0 +1,93 @@ +// 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.response; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.network.IpAddress; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.region.PortableIpRange; + +@EntityReference(value=PortableIpRange.class) +public class PortableIpRangeResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "portable IP range ID") + private String id; + + @SerializedName(ApiConstants.REGION_ID) + @Param(description = "Region Id in which portable ip range is provisioned") + private Integer regionId; + + @SerializedName(ApiConstants.GATEWAY) @Param(description="the gateway of the VLAN IP range") + private String gateway; + + @SerializedName(ApiConstants.NETMASK) @Param(description="the netmask of the VLAN IP range") + private String netmask; + + @SerializedName(ApiConstants.VLAN) @Param(description="the ID or VID of the VLAN.") + private String vlan; + + @SerializedName(ApiConstants.START_IP) @Param(description="the start ip of the portable IP range") + private String startIp; + + @SerializedName(ApiConstants.END_IP) @Param(description="the end ip of the portable IP range") + private String endIp; + + @SerializedName(ApiConstants.PORTABLE_IP_ADDRESS) + @Param(description="List of portable IP and association with zone/network/vpc details that are part of GSLB rule", responseObject = PortableIpResponse.class) + private List portableIpResponses; + + public void setId(String id) { + this.id = id; + } + + public void setRegionId(int regionId) { + this.regionId = regionId; + } + + public void setStartIp(String startIp) { + this.startIp = startIp; + } + + public void setEndIp(String endIp) { + this.endIp = endIp; + } + + public void setNetmask(String netmask) { + this.netmask = netmask; + } + + public void setGateway(String gateway) { + this.gateway = gateway; + } + + public void setVlan(String vlan) { + this.vlan = vlan; + } + + public void setPortableIpResponses(List portableIpResponses) { + this.portableIpResponses = portableIpResponses; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/api/response/PortableIpResponse.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/response/PortableIpResponse.java b/api/src/org/apache/cloudstack/api/response/PortableIpResponse.java new file mode 100644 index 0000000..0ccbcc3 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/PortableIpResponse.java @@ -0,0 +1,106 @@ +// 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.response; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.network.IpAddress; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpRange; + +@EntityReference(value=PortableIp.class) +public class PortableIpResponse extends BaseResponse { + + @SerializedName(ApiConstants.REGION_ID) + @Param(description = "Region Id in which global load balancer is created") + private Integer regionId; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="public IP address") + private String ipAddress; + + @SerializedName(ApiConstants.ZONE_ID) @Param(description="the ID of the zone the public IP address belongs to") + private String zoneId; + + @SerializedName(ApiConstants.NETWORK_ID) @Param(description="the ID of the Network where ip belongs to") + private String networkId; + + @SerializedName(ApiConstants.VPC_ID) @Param(description="VPC the ip belongs to") + private String vpcId; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) @Param(description="the physical network this belongs to") + private String physicalNetworkId; + + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the account ID the portable IP address is associated with") + private String accountId; + + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain ID the portable IP address is associated with") + private String domainId; + + @SerializedName("allocated") @Param(description="date the portal IP address was acquired") + private Date allocated; + + @SerializedName(ApiConstants.STATE) @Param(description="State of the ip address. Can be: Allocatin, Allocated and Releasing") + private String state; + + public void setRegionId(Integer regionId) { + this.regionId = regionId; + } + + public void setAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public void setAssociatedDataCenterId(String zoneId) { + this.zoneId = zoneId; + } + + public void setAssociatedWithNetworkId(String networkId) { + this.networkId = networkId; + } + + public void setAssociatedWithVpcId(String vpcId) { + this.vpcId = vpcId; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public void setAllocatedToAccountId(String accountId) { + this.accountId = accountId; + } + + public void setAllocatedInDomainId(String domainId) { + this.domainId = domainId; + } + + public void setAllocatedTime(Date allocatedTimetime) { + this.allocated = allocatedTimetime; + } + + public void setState(String state) { + this.state = state; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/region/PortableIp.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/region/PortableIp.java b/api/src/org/apache/cloudstack/region/PortableIp.java new file mode 100644 index 0000000..b95071e --- /dev/null +++ b/api/src/org/apache/cloudstack/region/PortableIp.java @@ -0,0 +1,58 @@ +// 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.region; + +import java.util.Date; + +import com.cloud.utils.net.Ip; +import org.apache.cloudstack.api.InternalIdentity; + +public interface PortableIp extends InternalIdentity { + + enum State { + Allocating, // The IP Address is being propagated to other network elements and is not ready for use yet. + Allocated, // The IP address is in used. + Releasing, // The IP address is being released for other network elements and is not ready for allocation. + Free // The IP address is ready to be allocated. + } + + Long getAllocatedToAccountId(); + + Long getAllocatedInDomainId(); + + Date getAllocatedTime(); + + State getState(); + + int getRegionId(); + + Long getAssociatedDataCenterId(); + + Long getAssociatedWithNetworkId(); + + Long getAssociatedWithVpcId(); + + Long getPhysicalNetworkId(); + + String getAddress(); + + String getVlan(); + + String getNetmask(); + + String getGateway(); +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/api/src/org/apache/cloudstack/region/PortableIpRange.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/region/PortableIpRange.java b/api/src/org/apache/cloudstack/region/PortableIpRange.java new file mode 100644 index 0000000..413a540 --- /dev/null +++ b/api/src/org/apache/cloudstack/region/PortableIpRange.java @@ -0,0 +1,38 @@ +// 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.region; + +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface PortableIpRange extends InfrastructureEntity, InternalIdentity, Identity { + + public final static String UNTAGGED = "untagged"; + + public String getVlanTag(); + + public String getGateway(); + + public String getNetmask(); + + public int getRegionId(); + + public String getIpRange(); + +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/client/tomcatconf/applicationContext.xml.in ---------------------------------------------------------------------- diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index b500fde..edf83a9 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -271,6 +271,8 @@ + + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/client/tomcatconf/commands.properties.in ---------------------------------------------------------------------- diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 68a7511..fd5479f 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -628,6 +628,11 @@ addCiscoAsa1000vResource=1 deleteCiscoAsa1000vResource=1 listCiscoAsa1000vResources=1 +#### portable public IP commands +createPortableIpRange=1 +deletePortableIpRange=1 +listPortableIpRanges=1 + #### Internal LB VM commands stopInternalLoadBalancerVM=1 startInternalLoadBalancerVM=1 @@ -635,3 +640,4 @@ listInternalLoadBalancerVMs=1 ### Network Isolation methods listing listNetworkIsolationMethods=1 + http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/engine/schema/src/com/cloud/network/dao/IPAddressVO.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/com/cloud/network/dao/IPAddressVO.java b/engine/schema/src/com/cloud/network/dao/IPAddressVO.java index ae27e95..5eb2500 100644 --- a/engine/schema/src/com/cloud/network/dao/IPAddressVO.java +++ b/engine/schema/src/com/cloud/network/dao/IPAddressVO.java @@ -111,6 +111,8 @@ public class IPAddressVO implements IpAddress { @Column(name="dnat_vmip") private String vmIp; + @Column(name="is_portable") + private boolean portable = false; protected IPAddressVO() { this.uuid = UUID.randomUUID().toString(); @@ -134,6 +136,19 @@ public class IPAddressVO implements IpAddress { this.uuid = UUID.randomUUID().toString(); } + public IPAddressVO(Ip address, long dataCenterId, Long networkId, Long vpcId, long physicalNetworkId, long sourceNetworkId, + long vlanDbId, boolean portable) { + this.address = address; + this.dataCenterId = dataCenterId; + this.associatedWithNetworkId = networkId; + this.vpcId = vpcId; + this.physicalNetworkId = physicalNetworkId; + this.sourceNetworkId = sourceNetworkId; + this.vlanId = vlanDbId; + this.portable = portable; + this.uuid = UUID.randomUUID().toString(); + } + public long getMacAddress() { return macAddress; } @@ -283,6 +298,15 @@ public class IPAddressVO implements IpAddress { this.system = isSystem; } + @Override + public boolean isPortable() { + return portable; + } + + public void setPortable(boolean portable) { + this.portable = portable; + } + @Override public Long getVpcId() { return vpcId; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/server/src/com/cloud/api/ApiResponseHelper.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 49e36dc..fc1c6a0 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -49,6 +49,8 @@ import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpRange; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.ApplicationLoadBalancerInstanceResponse; import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse; @@ -98,6 +100,8 @@ import org.apache.cloudstack.api.response.NicResponse; import org.apache.cloudstack.api.response.NicSecondaryIpResponse; import org.apache.cloudstack.api.response.PhysicalNetworkResponse; import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.PortableIpRangeResponse; +import org.apache.cloudstack.api.response.PortableIpResponse; import org.apache.cloudstack.api.response.PrivateGatewayResponse; import org.apache.cloudstack.api.response.ProjectAccountResponse; import org.apache.cloudstack.api.response.ProjectInvitationResponse; @@ -722,6 +726,8 @@ public class ApiResponseHelper implements ResponseGenerator { } } + ipResponse.setPortable(ipAddr.isPortable()); + //set tag information List tags = ApiDBUtils.listByResourceTypeAndId(TaggedResourceType.PublicIpAddress, ipAddr.getId()); List tagResponses = new ArrayList(); @@ -3824,6 +3830,69 @@ public class ApiResponseHelper implements ResponseGenerator { } } + @Override + public PortableIpRangeResponse createPortableIPRangeResponse(PortableIpRange ipRange) { + PortableIpRangeResponse response = new PortableIpRangeResponse(); + response.setId(ipRange.getUuid()); + String ipRangeStr = ipRange.getIpRange(); + if (ipRangeStr != null) { + String[] range = ipRangeStr.split("-"); + response.setStartIp(range[0]); + response.setEndIp(range[1]); + } + response.setVlan(ipRange.getVlanTag()); + response.setGateway(ipRange.getGateway()); + response.setNetmask(ipRange.getNetmask()); + response.setRegionId(ipRange.getRegionId()); + return response; + } + + @Override + public PortableIpResponse createPortableIPResponse(PortableIp portableIp) { + PortableIpResponse response = new PortableIpResponse(); + response.setAddress(portableIp.getAddress()); + Long accountId = portableIp.getAllocatedInDomainId(); + if (accountId != null) { + Account account = ApiDBUtils.findAccountById(accountId); + response.setAllocatedToAccountId(account.getAccountName()); + Domain domain = ApiDBUtils.findDomainById(account.getDomainId()); + response.setAllocatedInDomainId(domain.getUuid()); + } + + response.setAllocatedTime(portableIp.getAllocatedTime()); + + if (portableIp.getAssociatedDataCenterId() != null) { + DataCenter zone = ApiDBUtils.findZoneById(portableIp.getAssociatedDataCenterId()); + if (zone != null) { + response.setAssociatedDataCenterId(zone.getUuid()); + } + } + + if (portableIp.getPhysicalNetworkId() != null) { + PhysicalNetwork pnw = ApiDBUtils.findPhysicalNetworkById(portableIp.getPhysicalNetworkId()); + if (pnw != null) { + response.setPhysicalNetworkId(pnw.getUuid()); + } + } + + if (portableIp.getAssociatedWithNetworkId() != null) { + Network ntwk = ApiDBUtils.findNetworkById(portableIp.getAssociatedWithNetworkId()); + if (ntwk != null) { + response.setAssociatedWithNetworkId(ntwk.getUuid()); + } + } + + if (portableIp.getAssociatedWithVpcId() != null) { + Vpc vpc = ApiDBUtils.findVpcById(portableIp.getAssociatedWithVpcId()); + if (vpc != null) { + response.setAssociatedWithVpcId(vpc.getUuid()); + } + } + + response.setState(portableIp.getState().name()); + + return response; + } @Override public InternalLoadBalancerElementResponse createInternalLbElementResponse(VirtualRouterProvider result) { @@ -3842,7 +3911,6 @@ public class ApiResponseHelper implements ResponseGenerator { return response; } - @Override public IsolationMethodResponse createIsolationMethodResponse(IsolationType method) { IsolationMethodResponse response = new IsolationMethodResponse(); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/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 52d6176..47c5482 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -43,6 +43,7 @@ import com.cloud.dc.*; import com.cloud.dc.dao.*; import com.cloud.user.*; import com.cloud.event.UsageEventUtils; +import com.cloud.utils.db.*; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.ApiConstants.LDAPParams; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; @@ -59,6 +60,9 @@ import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd; 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.region.CreatePortableIpRangeCmd; +import org.apache.cloudstack.api.command.admin.region.DeletePortableIpRangeCmd; +import org.apache.cloudstack.api.command.admin.region.ListPortableIpRangesCmd; 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; @@ -67,6 +71,8 @@ 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; import org.apache.cloudstack.api.command.user.network.ListNetworkOfferingsCmd; +import org.apache.cloudstack.region.*; +import org.apache.cloudstack.region.dao.RegionDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; @@ -186,10 +192,6 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.StringUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.crypt.DBEncryptionUtil; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.Filter; -import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.NicIpAlias; @@ -332,6 +334,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Inject VpcManager _vpcMgr; @Inject + PortableIpRangeDao _portableIpRangeDao; + @Inject + RegionDao _regionDao; + @Inject + PortableIpDao _portableIpDao; + @Inject ConfigurationServer _configServer; @Inject DcDetailsDao _dcDetailsDao; @@ -4718,4 +4726,153 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return null; } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_PORTABLE_IP_RANGE_CREATE, eventDescription = "creating portable ip range", async = false) + public PortableIpRange createPortableIpRange(CreatePortableIpRangeCmd cmd) throws ConcurrentOperationException { + Integer regionId = cmd.getRegionId(); + String startIP = cmd.getStartIp(); + String endIP = cmd.getEndIp(); + String gateway = cmd.getGateway(); + String netmask = cmd.getNetmask(); + Long userId = UserContext.current().getCallerUserId(); + String vlanId = cmd.getVlan(); + + Region region = _regionDao.findById(regionId); + if (region == null) { + throw new InvalidParameterValueException("Invalid region ID: " + regionId); + } + + if (!NetUtils.isValidIp(startIP) || !NetUtils.isValidIp(endIP) || !NetUtils.validIpRange(startIP, endIP)) { + throw new InvalidParameterValueException("Invalid portable ip range: " + startIP + "-" + endIP); + } + + if (!NetUtils.sameSubnet(startIP, gateway, netmask)) { + throw new InvalidParameterValueException("Please ensure that your start IP is in the same subnet as " + + "your portable IP range's gateway and as per the IP range's netmask."); + } + + if (!NetUtils.sameSubnet(endIP, gateway, netmask)) { + throw new InvalidParameterValueException("Please ensure that your end IP is in the same subnet as " + + "your portable IP range's gateway and as per the IP range's netmask."); + } + + if (checkOverlapPortableIpRange(regionId, startIP, endIP)) { + throw new InvalidParameterValueException("Ip range: " + startIP + "-" + endIP + " overlaps with a portable" + + " IP range already configured in the region " + regionId); + } + + if (vlanId == null) { + vlanId = Vlan.UNTAGGED; + } else { + if (!NetUtils.isValidVlan(vlanId)) { + throw new InvalidParameterValueException("Invalid vlan id " + vlanId); + } + } + GlobalLock portableIpLock = GlobalLock.getInternLock("PortablePublicIpRange"); + portableIpLock.lock(5); + Transaction txn = Transaction.currentTxn(); + txn.start(); + + PortableIpRangeVO portableIpRange = new PortableIpRangeVO(regionId, vlanId, gateway, netmask, startIP, endIP); + portableIpRange = _portableIpRangeDao.persist(portableIpRange); + + long startIpLong = NetUtils.ip2Long(startIP); + long endIpLong = NetUtils.ip2Long(endIP); + while(startIpLong <= endIpLong) { + PortableIpVO portableIP = new PortableIpVO(regionId, portableIpRange.getId(), vlanId, + gateway, netmask,NetUtils.long2Ip(startIpLong)); + _portableIpDao.persist(portableIP); + startIpLong++; + } + + txn.commit(); + portableIpLock.unlock(); + return portableIpRange; + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_PORTABLE_IP_RANGE_DELETE, eventDescription = "deleting portable ip range", async = false) + public boolean deletePortableIpRange(DeletePortableIpRangeCmd cmd) { + long rangeId = cmd.getId(); + PortableIpRangeVO portableIpRange = _portableIpRangeDao.findById(rangeId); + if (portableIpRange == null) { + throw new InvalidParameterValueException("Please specify a valid portable IP range id."); + } + + List fullIpRange = _portableIpDao.listByRangeId(portableIpRange.getId()); + List freeIpRange = _portableIpDao.listByRangeIdAndState(portableIpRange.getId(), PortableIp.State.Free); + + if (fullIpRange != null && freeIpRange != null) { + if (fullIpRange.size() == freeIpRange.size()) { + _portableIpRangeDao.expunge(portableIpRange.getId()); + return true; + } else { + throw new InvalidParameterValueException("Can't delete portable IP range as there are IP's assigned."); + } + } + + return false; + } + + @Override + public List listPortableIpRanges(ListPortableIpRangesCmd cmd) { + Integer regionId = cmd.getRegionIdId(); + Long rangeId = cmd.getPortableIpRangeId(); + + List ranges = new ArrayList(); + if (regionId != null) { + Region region = _regionDao.findById(regionId); + if (region == null) { + throw new InvalidParameterValueException("Invalid region ID: " + regionId); + } + return _portableIpRangeDao.listByRegionId(regionId); + } + + if (rangeId != null) { + PortableIpRangeVO range = _portableIpRangeDao.findById(rangeId); + if (range == null) { + throw new InvalidParameterValueException("Invalid portable IP range ID: " + regionId); + } + ranges.add(range); + return ranges; + } + + return _portableIpRangeDao.listAll(); + } + + @Override + public List listPortableIps(long id) { + + PortableIpRangeVO portableIpRange = _portableIpRangeDao.findById(id); + if (portableIpRange == null) { + throw new InvalidParameterValueException("Please specify a valid portable IP range id."); + } + + return _portableIpDao.listByRangeId(portableIpRange.getId()); + } + + private boolean checkOverlapPortableIpRange(int regionId, String newStartIpStr, String newEndIpStr) { + long newStartIp = NetUtils.ip2Long(newStartIpStr); + long newEndIp = NetUtils.ip2Long(newEndIpStr); + + List existingPortableIPRanges = _portableIpRangeDao.listByRegionId(regionId); + for (PortableIpRangeVO portableIpRange : existingPortableIPRanges) { + String ipRangeStr = portableIpRange.getIpRange(); + String[] range = ipRangeStr.split("-"); + long startip = NetUtils.ip2Long(range[0]); + long endIp = NetUtils.ip2Long(range[1]); + + if ((newStartIp >= startip && newStartIp <= endIp) || (newEndIp >= startip && newEndIp <= endIp)) { + return true; + } + + if ((startip >= newStartIp && startip <= newEndIp) || (endIp >= newStartIp && endIp <= newEndIp)) { + return true; + } + } + return false; + } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/server/src/com/cloud/network/NetworkManager.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 08198ee..1e4fb84 100755 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -59,6 +59,7 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.region.PortableIp; /** * NetworkManager manages the network for the different end users. @@ -240,6 +241,16 @@ public interface NetworkManager { IPAddressVO associateIPToGuestNetwork(long ipAddrId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, ResourceUnavailableException, InsufficientAddressCapacityException, ConcurrentOperationException; + IPAddressVO associatePortableIPToGuestNetwork(long ipAddrId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, ResourceUnavailableException, + InsufficientAddressCapacityException, ConcurrentOperationException; + + IPAddressVO disassociatePortableIPToGuestNetwork(long ipAddrId, long networkId) throws ResourceAllocationException, ResourceUnavailableException, + InsufficientAddressCapacityException, ConcurrentOperationException; + + boolean isPortableIpTransferableFromNetwork(long ipAddrId, long networkId); + + void transferPortableIP(long ipAddrId, long currentNetworkId, long newNetworkId) throws ResourceAllocationException, ResourceUnavailableException, + InsufficientAddressCapacityException, ConcurrentOperationException;; /** * @param network @@ -325,6 +336,9 @@ public interface NetworkManager { DataCenter zone) throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException; + IpAddress allocatePortableIp(Account ipOwner, Account caller, long dcId, Long networkId, Long vpcID) + throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException; + Map finalizeServicesAndProvidersForNetwork(NetworkOffering offering, Long physicalNetworkId); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/d6452be8/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 40fc3d3..b3df0da 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -105,6 +105,10 @@ import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.dao.*; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.region.PortableIp; +import org.apache.cloudstack.region.PortableIpDao; +import org.apache.cloudstack.region.PortableIpVO; +import org.apache.cloudstack.region.Region; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -251,6 +255,8 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L UserIpv6AddressDao _ipv6Dao; @Inject Ipv6AddressManager _ipv6Mgr; + @Inject + PortableIpDao _portableIpDao; protected StateMachine2 _stateMachine; private final HashMap _systemNetworks = new HashMap(5); @@ -702,6 +708,62 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L return ip; } + @Override + @DB + public IpAddress allocatePortableIp(Account ipOwner, Account caller, long dcId, Long networkId, Long vpcID) + throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException { + + Transaction txn = Transaction.currentTxn(); + GlobalLock portableIpLock = GlobalLock.getInternLock("PortablePublicIpRange"); + PortableIpVO allocatedPortableIp; + IPAddressVO ipaddr; + + try { + portableIpLock.lock(5); + + txn.start(); + //TODO: get the region ID corresponding to running management server + List portableIpVOs = _portableIpDao.listByRegionIdAndState(1, PortableIp.State.Free); + if (portableIpVOs == null || portableIpVOs.isEmpty()) { + InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException + ("Unable to find available portable IP addresses", Region.class, new Long(1)); + throw ex; + } + + // allocate first portable IP to the user + allocatedPortableIp = portableIpVOs.get(0); + allocatedPortableIp.setAllocatedTime(new Date()); + allocatedPortableIp.setAllocatedToAccountId(ipOwner.getAccountId()); + allocatedPortableIp.setAllocatedInDomainId(ipOwner.getDomainId()); + allocatedPortableIp.setState(PortableIp.State.Allocated); + _portableIpDao.update(allocatedPortableIp.getId(), allocatedPortableIp); + + // provision portable IP range VLAN + long physicalNetworkId = _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(dcId, TrafficType.Public).getId(); + Network network = _networkModel.getNetwork(physicalNetworkId); + String range = allocatedPortableIp.getAddress() + "-" + allocatedPortableIp.getAddress(); + VlanVO vlan = new VlanVO(VlanType.VirtualNetwork, allocatedPortableIp.getVlan(), allocatedPortableIp.getGateway(), + allocatedPortableIp.getNetmask(), dcId, range, network.getId(), network.getId(), null, null, null); + vlan = _vlanDao.persist(vlan); + + // provision the portable IP in to user_ip_address table + ipaddr = new IPAddressVO(new Ip(allocatedPortableIp.getAddress()), dcId, networkId, vpcID, network.getId(), + network.getId(), vlan.getId(), true); + ipaddr.setState(State.Allocated); + ipaddr.setAllocatedTime(new Date()); + ipaddr.setAllocatedInDomainId(ipOwner.getDomainId()); + ipaddr.setAllocatedToAccountId(ipOwner.getId()); + ipaddr= _ipAddressDao.persist(ipaddr); + + txn.commit(); + + } finally { + portableIpLock.unlock(); + } + + return ipaddr; + } + protected IPAddressVO getExistingSourceNatInNetwork(long ownerId, Long networkId) { List addrs = _networkModel.listPublicIpsAssignedToGuestNtwk(ownerId, networkId, true); @@ -741,12 +803,14 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } DataCenter zone = _configMgr.getZone(network.getDataCenterId()); - if (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced) { - if (isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())) { - _accountMgr.checkAccess(UserContext.current().getCaller(), AccessType.UseNetwork, false, network); - } else { - throw new InvalidParameterValueException("IP can be associated with guest network of 'shared' type only if " + - "network services Source Nat, Static Nat, Port Forwarding, Load balancing, firewall are enabled in the network"); + if (zone.getNetworkType() == NetworkType.Advanced) { + if (network.getGuestType() == Network.GuestType.Shared) { + if (isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())) { + _accountMgr.checkAccess(UserContext.current().getCaller(), AccessType.UseNetwork, false, network); + } else { + throw new InvalidParameterValueException("IP can be associated with guest network of 'shared' type only if " + + "network services Source Nat, Static Nat, Port Forwarding, Load balancing, firewall are enabled in the network"); + } } } else { _accountMgr.checkAccess(caller, null, true, ipToAssoc); @@ -844,6 +908,162 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } } + @Override + public IPAddressVO associatePortableIPToGuestNetwork(long ipAddrId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, ResourceUnavailableException, + InsufficientAddressCapacityException, ConcurrentOperationException { + return associateIPToGuestNetwork(ipAddrId, networkId, releaseOnFailure); + } + + @DB + @Override + public IPAddressVO disassociatePortableIPToGuestNetwork(long ipId, long networkId) + throws ResourceAllocationException, ResourceUnavailableException, + InsufficientAddressCapacityException, ConcurrentOperationException { + + Account caller = UserContext.current().getCaller(); + Account owner = null; + + Network network = _networksDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException("Invalid network id is given"); + } + + IPAddressVO ipToAssoc = _ipAddressDao.findById(ipId); + if (ipToAssoc != null) { + + if (ipToAssoc.getAssociatedWithNetworkId() == null) { + throw new InvalidParameterValueException("IP " + ipToAssoc + " is not associated with any network"); + } + + if (ipToAssoc.getAssociatedWithNetworkId() != network.getId()) { + throw new InvalidParameterValueException("IP " + ipToAssoc + " is not associated with network id" + networkId); + } + + DataCenter zone = _configMgr.getZone(network.getDataCenterId()); + if (zone.getNetworkType() == NetworkType.Advanced) { + if (network.getGuestType() == Network.GuestType.Shared) { + assert (isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())); + _accountMgr.checkAccess(UserContext.current().getCaller(), AccessType.UseNetwork, false, network); + } + } else { + _accountMgr.checkAccess(caller, null, true, ipToAssoc); + } + owner = _accountMgr.getAccount(ipToAssoc.getAllocatedToAccountId()); + } else { + s_logger.debug("Unable to find ip address by id: " + ipId); + return null; + } + + DataCenter zone = _configMgr.getZone(network.getDataCenterId()); + + // Check that network belongs to IP owner - skip this check + // - if zone is basic zone as there is just one guest network, + // - if shared network in Advanced zone + // - and it belongs to the system + if (network.getAccountId() != owner.getId()) { + if (zone.getNetworkType() != NetworkType.Basic && !(zone.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Shared)) { + throw new InvalidParameterValueException("The owner of the network is not the same as owner of the IP"); + } + } + + // Check if IP has any services (rules) associated in the network + List ipList = new ArrayList(); + PublicIp publicIp = PublicIp.createFromAddrAndVlan(ipToAssoc, _vlanDao.findById(ipToAssoc.getVlanId())); + ipList.add(publicIp); + Map> ipToServices = _networkModel.getIpToServices(ipList, false, true); + if (ipToServices != null & !ipToServices.isEmpty()) { + Set services = ipToServices.get(publicIp); + if (services != null && !services.isEmpty()) { + throw new InvalidParameterValueException("IP " + ipToAssoc + " has services and rules associated in the network " + networkId); + } + } + + IPAddressVO ip = _ipAddressDao.findById(ipId); + ip.setAssociatedWithNetworkId(null); + _ipAddressDao.update(ipId, ip); + + try { + boolean success = applyIpAssociations(network, false); + if (success) { + s_logger.debug("Successfully associated ip address " + ip.getAddress().addr() + " to network " + network); + } else { + s_logger.warn("Failed to associate ip address " + ip.getAddress().addr() + " to network " + network); + } + return ip; + } finally { + + } + } + + @Override + public boolean isPortableIpTransferableFromNetwork(long ipAddrId, long networkId) { + Network network = _networksDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException("Invalid network id is given"); + } + + IPAddressVO ip = _ipAddressDao.findById(ipAddrId); + if (ip == null) { + throw new InvalidParameterValueException("Invalid network id is given"); + } + + // Check if IP has any services (rules) associated in the network + List ipList = new ArrayList(); + PublicIp publicIp = PublicIp.createFromAddrAndVlan(ip, _vlanDao.findById(ip.getVlanId())); + ipList.add(publicIp); + Map> ipToServices = _networkModel.getIpToServices(ipList, false, true); + if (ipToServices != null & !ipToServices.isEmpty()) { + Set ipServices = ipToServices.get(publicIp); + if (ipServices != null && !ipServices.isEmpty()) { + return false; + } + } + + return true; + } + + @DB + @Override + public void transferPortableIP(long ipAddrId, long currentNetworkId, long newNetworkId) throws ResourceAllocationException, ResourceUnavailableException, + InsufficientAddressCapacityException, ConcurrentOperationException { + + Network srcNetwork = _networksDao.findById(currentNetworkId); + if (srcNetwork == null) { + throw new InvalidParameterValueException("Invalid source network id " + currentNetworkId +" is given"); + } + + Network dstNetwork = _networksDao.findById(newNetworkId); + if (dstNetwork == null) { + throw new InvalidParameterValueException("Invalid source network id " + newNetworkId +" is given"); + } + + IPAddressVO ip = _ipAddressDao.findById(ipAddrId); + if (ip == null) { + throw new InvalidParameterValueException("Invalid portable ip address id is given"); + } + + Transaction txn = Transaction.currentTxn(); + txn.start(); + + assert(isPortableIpTransferableFromNetwork(ipAddrId, currentNetworkId)); + + if (srcNetwork.getVpcId() != null) { + _vpcMgr.unassignIPFromVpcNetwork(ipAddrId, currentNetworkId); + } else { + disassociatePortableIPToGuestNetwork(ipAddrId, currentNetworkId); + } + + associatePortableIPToGuestNetwork(ipAddrId, newNetworkId, false); + + if (dstNetwork.getVpcId() != null) { + ip.setVpcId(dstNetwork.getVpcId()); + } else { + ip.setVpcId(null); + } + + _ipAddressDao.update(ipAddrId, ip); + txn.commit(); + } @Override @DB @@ -884,12 +1104,42 @@ public class NetworkManagerImpl extends ManagerBase implements NetworkManager, L } if (success) { + if (ip.isPortable()) { + releasePortableIpAddress(addrId); + } s_logger.debug("Released a public ip id=" + addrId); } return success; } + @DB + private void releasePortableIpAddress(long addrId) { + Transaction txn = Transaction.currentTxn(); + GlobalLock portableIpLock = GlobalLock.getInternLock("PortablePublicIpRange"); + + txn.start(); + try { + portableIpLock.lock(5); + IPAddressVO ip = _ipAddressDao.findById(addrId); + + // unassign portable IP + PortableIpVO portableIp = _portableIpDao.findByIpAddress(ip.getAddress().addr()); + _portableIpDao.unassignIpAddress(portableIp.getId()); + + // removed the provisioned vlan + VlanVO vlan = _vlanDao.findById(ip.getVlanId()); + _vlanDao.expunge(vlan.getId()); + + // remove the provisioned public ip address + _ipAddressDao.expunge(ip.getId()); + + txn.commit(); + } finally { + portableIpLock.releaseRef(); + } + } + @Override @DB public boolean configure(final String name, final Map params) throws ConfigurationException {