cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From muralire...@apache.org
Subject [2/2] git commit: updated refs/heads/master to d6452be
Date Mon, 20 May 2013 14:38:26 GMT
CLOUDSTACK-652: meging 'portable public ip' feature

Squashed commit of the following:

commit f244f9ce7982db16984dd87c31545f1c0240c704
Merge: 993cbb0 f5c8e38
Author: Murali Reddy <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
Date:   Mon May 13 20:05:29 2013 +0530

    added integration tests for testing portable ip ranges

commit 895a27c2771dbb497ecc6fe0d212589f012a48d8
Author: Murali Reddy <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
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 <muralimmreddy@gmail.com>
Authored: Mon May 20 20:05:47 2013 +0530
Committer: Murali Reddy <muralimmreddy@gmail.com>
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<? extends PortableIpRange> listPortableIpRanges(ListPortableIpRangesCmd cmd);
+
+    List<? extends PortableIp> 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<PortableIpRangeResponse> response = new ListResponse<PortableIpRangeResponse>();
+        List<PortableIpRangeResponse> responses = new ArrayList<PortableIpRangeResponse>();
+        List<? extends PortableIpRange> portableIpRanges = new ArrayList<PortableIpRange>();
+
+        portableIpRanges = _configService.listPortableIpRanges(this);
+        if (portableIpRanges != null && !portableIpRanges.isEmpty()) {
+            for (PortableIpRange range : portableIpRanges) {
+                PortableIpRangeResponse rangeResponse = _responseGenerator.createPortableIPRangeResponse(range);
+
+                List<? extends PortableIp> portableIps = _configService.listPortableIps(range.getId());
+                if (portableIps != null && !portableIps.isEmpty()) {
+                    List<PortableIpResponse> portableIpResponses = new ArrayList<PortableIpResponse>();
+                    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<ResourceTagResponse> 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<PortableIpResponse> 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<PortableIpResponse> 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 @@
   <bean id="physicalNetworkTrafficTypeDaoImpl" class="com.cloud.network.dao.PhysicalNetworkTrafficTypeDaoImpl" />
   <bean id="podVlanDaoImpl" class="com.cloud.dc.dao.PodVlanDaoImpl" />
   <bean id="podVlanMapDaoImpl" class="com.cloud.dc.dao.PodVlanMapDaoImpl" />
+  <bean id="PortableIpDaoImpl" class="org.apache.cloudstack.region.PortableIpDaoImpl" />
+  <bean id="PortableIpRangeDaoImpl" class="org.apache.cloudstack.region.PortableIpRangeDaoImpl" />
   <bean id="portForwardingRulesDaoImpl" class="com.cloud.network.rules.dao.PortForwardingRulesDaoImpl" />
   <bean id="portProfileDaoImpl" class="com.cloud.network.dao.PortProfileDaoImpl" />
   <bean id="primaryDataStoreDaoImpl" class="org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDaoImpl" />

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<? extends ResourceTag> tags = ApiDBUtils.listByResourceTypeAndId(TaggedResourceType.PublicIpAddress, ipAddr.getId());
         List<ResourceTagResponse> tagResponses = new ArrayList<ResourceTagResponse>();
@@ -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<PortableIpVO> fullIpRange = _portableIpDao.listByRangeId(portableIpRange.getId());
+        List<PortableIpVO> 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<? extends PortableIpRange> listPortableIpRanges(ListPortableIpRangesCmd cmd) {
+        Integer regionId = cmd.getRegionIdId();
+        Long rangeId = cmd.getPortableIpRangeId();
+
+        List <PortableIpRangeVO> ranges = new ArrayList<PortableIpRangeVO>();
+        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<? extends PortableIp> 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<PortableIpRangeVO> 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<String, String> 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<Network.State, Network.Event, Network> _stateMachine;
     private final HashMap<String, NetworkOfferingVO> _systemNetworks = new HashMap<String, NetworkOfferingVO>(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<PortableIpVO> 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<? extends IpAddress> 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<PublicIpAddress> ipList = new ArrayList<PublicIpAddress>();
+        PublicIp publicIp = PublicIp.createFromAddrAndVlan(ipToAssoc, _vlanDao.findById(ipToAssoc.getVlanId()));
+        ipList.add(publicIp);
+        Map<PublicIpAddress, Set<Service>> ipToServices = _networkModel.getIpToServices(ipList, false, true);
+        if (ipToServices != null & !ipToServices.isEmpty()) {
+            Set<Service> 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<PublicIpAddress> ipList = new ArrayList<PublicIpAddress>();
+        PublicIp publicIp = PublicIp.createFromAddrAndVlan(ip, _vlanDao.findById(ip.getVlanId()));
+        ipList.add(publicIp);
+        Map<PublicIpAddress, Set<Service>> ipToServices = _networkModel.getIpToServices(ipList, false, true);
+        if (ipToServices != null & !ipToServices.isEmpty()) {
+            Set<Service> 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<String, Object> params) throws ConfigurationException {


Mime
View raw message