DaanHoogland closed pull request #2048: CLOUDSTACK-9880: Expansion of 
Management IP Range.
URL: https://github.com/apache/cloudstack/pull/2048
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/api/src/com/cloud/configuration/ConfigurationService.java 
b/api/src/com/cloud/configuration/ConfigurationService.java
index b13122208ee..5af44ed18f4 100644
--- a/api/src/com/cloud/configuration/ConfigurationService.java
+++ b/api/src/com/cloud/configuration/ConfigurationService.java
@@ -19,7 +19,9 @@
 import java.util.List;
 
 import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
+import 
org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
 import 
org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
+import 
org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd;
 import 
org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
 import 
org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd;
@@ -160,6 +162,19 @@
      */
     Pod createPod(long zoneId, String name, String startIp, String endIp, 
String gateway, String netmask, String allocationState);
 
+    /**
+     * Creates a mutual exclusive IP range in the pod with same gateway, 
netmask.
+     * @param cmd - The command specifying pod ID, start IP, end IP, gateway, 
netmask.
+     * @return The new range if successful, null otherwise.
+     */
+    Pod createPodIpRange(CreateManagementNetworkIpRangeCmd cmd);
+
+    /**
+     * Deletes a mutually exclusive IP range in the pod.
+     * @param cmd - The command specifying pod ID, start IP, end IP.
+     */
+    void deletePodIpRange(DeleteManagementNetworkIpRangeCmd cmd) throws 
ResourceUnavailableException, ConcurrentOperationException;
+
     /**
      * Edits a pod in the database. Will not allow you to edit pods that are 
being used anywhere in the system.
      *
diff --git a/api/src/com/cloud/dc/StorageNetworkIpRange.java 
b/api/src/com/cloud/dc/StorageNetworkIpRange.java
index 2b50ca1594e..03d4f44de48 100644
--- a/api/src/com/cloud/dc/StorageNetworkIpRange.java
+++ b/api/src/com/cloud/dc/StorageNetworkIpRange.java
@@ -21,20 +21,19 @@
 import org.apache.cloudstack.api.InternalIdentity;
 
 public interface StorageNetworkIpRange extends InfrastructureEntity, 
InternalIdentity, Identity {
-
     Integer getVlan();
 
-    String getPodUuid();
+    String getGateway();
+
+    String getNetmask();
 
     String getStartIp();
 
     String getEndIp();
 
-    String getNetworkUuid();
-
     String getZoneUuid();
 
-    String getNetmask();
+    String getPodUuid();
 
-    String getGateway();
+    String getNetworkUuid();
 }
diff --git a/api/src/com/cloud/event/EventTypes.java 
b/api/src/com/cloud/event/EventTypes.java
index ec3f3ac6d10..d5d11e87702 100644
--- a/api/src/com/cloud/event/EventTypes.java
+++ b/api/src/com/cloud/event/EventTypes.java
@@ -296,6 +296,9 @@
     public static final String EVENT_VLAN_IP_RANGE_DEDICATE = 
"VLAN.IP.RANGE.DEDICATE";
     public static final String EVENT_VLAN_IP_RANGE_RELEASE = 
"VLAN.IP.RANGE.RELEASE";
 
+    public static final String EVENT_MANAGEMENT_IP_RANGE_CREATE = 
"MANAGEMENT.IP.RANGE.CREATE";
+    public static final String EVENT_MANAGEMENT_IP_RANGE_DELETE = 
"MANAGEMENT.IP.RANGE.DELETE";
+
     public static final String EVENT_STORAGE_IP_RANGE_CREATE = 
"STORAGE.IP.RANGE.CREATE";
     public static final String EVENT_STORAGE_IP_RANGE_DELETE = 
"STORAGE.IP.RANGE.DELETE";
     public static final String EVENT_STORAGE_IP_RANGE_UPDATE = 
"STORAGE.IP.RANGE.UPDATE";
@@ -761,6 +764,9 @@
         entityEventDetails.put(EVENT_VLAN_IP_RANGE_DEDICATE, Vlan.class);
         entityEventDetails.put(EVENT_VLAN_IP_RANGE_RELEASE, Vlan.class);
 
+        entityEventDetails.put(EVENT_MANAGEMENT_IP_RANGE_CREATE, Pod.class);
+        entityEventDetails.put(EVENT_MANAGEMENT_IP_RANGE_DELETE, Pod.class);
+
         entityEventDetails.put(EVENT_STORAGE_IP_RANGE_CREATE, 
StorageNetworkIpRange.class);
         entityEventDetails.put(EVENT_STORAGE_IP_RANGE_DELETE, 
StorageNetworkIpRange.class);
         entityEventDetails.put(EVENT_STORAGE_IP_RANGE_UPDATE, 
StorageNetworkIpRange.class);
diff --git 
a/api/src/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java
 
b/api/src/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java
new file mode 100644
index 00000000000..e5bfc07c699
--- /dev/null
+++ 
b/api/src/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java
@@ -0,0 +1,145 @@
+// 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.network;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.ApiArgValidator;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.PodResponse;
+
+import com.cloud.dc.Pod;
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.user.Account;
+
+@APICommand(name = CreateManagementNetworkIpRangeCmd.APINAME,
+        description = "Creates a Management network IP range.",
+        responseObject = PodResponse.class,
+        since = "4.11.0.0",
+        requestHasSensitiveInfo = false,
+        responseHasSensitiveInfo = false,
+        authorized = {RoleType.Admin})
+public class CreateManagementNetworkIpRangeCmd extends BaseAsyncCmd {
+    public static final Logger s_logger = 
Logger.getLogger(CreateManagementNetworkIpRangeCmd.class);
+
+    public static final String APINAME = "createManagementNetworkIpRange";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+    @Parameter(name = ApiConstants.POD_ID,
+            type = CommandType.UUID,
+            entityType = PodResponse.class,
+            required = true,
+            description = "UUID of POD, where the IP range belongs to.",
+            validations = {ApiArgValidator.PositiveNumber})
+    private Long podId;
+
+    @Parameter(name = ApiConstants.GATEWAY,
+            type = CommandType.STRING,
+            required = true,
+            description = "The gateway for the management network.",
+            validations = {ApiArgValidator.NotNullOrEmpty})
+    private String gateway;
+
+    @Parameter(name = ApiConstants.NETMASK,
+            type = CommandType.STRING,
+            required = true,
+            description = "The netmask for the management network.",
+            validations = {ApiArgValidator.NotNullOrEmpty})
+    private String netmask;
+
+    @Parameter(name = ApiConstants.START_IP,
+            type = CommandType.STRING,
+            required = true,
+            description = "The starting IP address.",
+            validations = {ApiArgValidator.NotNullOrEmpty})
+    private String startIp;
+
+    @Parameter(name = ApiConstants.END_IP,
+            type = CommandType.STRING,
+            description = "The ending IP address.")
+    private String endIp;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getPodId() {
+        return podId;
+    }
+
+    public String getGateWay() {
+        return gateway;
+    }
+
+    public String getNetmask() {
+        return netmask;
+    }
+
+    public String getStartIp() {
+        return startIp;
+    }
+
+    public String getEndIp() {
+        return endIp;
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_MANAGEMENT_IP_RANGE_CREATE;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Creating management ip range from " + getStartIp() + " to " + 
getEndIp() + " and gateway=" + getGateWay() + ", netmask=" + getNetmask() + " 
of pod=" + getPodId();
+    }
+
+    @Override
+    public void execute() throws ResourceUnavailableException, 
InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
+            ResourceAllocationException {
+        Pod result = _configService.createPodIpRange(this);
+        if (result != null) {
+            PodResponse response = 
_responseGenerator.createPodResponse(result, false);
+            response.setResponseName(getCommandName());
+            this.setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed 
to create Pod IP Range.");
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseAsyncCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+}
diff --git 
a/api/src/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java
 
b/api/src/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java
new file mode 100644
index 00000000000..acb9e7a4678
--- /dev/null
+++ 
b/api/src/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java
@@ -0,0 +1,129 @@
+// 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.network;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.ApiArgValidator;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.PodResponse;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.SuccessResponse;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.user.Account;
+
+@APICommand(name = DeleteManagementNetworkIpRangeCmd.APINAME,
+        description = "Deletes a management network IP range. This action is 
only allowed when no IPs in this range are allocated.",
+        responseObject = SuccessResponse.class,
+        since = "4.11.0.0",
+        requestHasSensitiveInfo = false,
+        responseHasSensitiveInfo = false,
+        authorized = {RoleType.Admin})
+public class DeleteManagementNetworkIpRangeCmd extends BaseAsyncCmd {
+    public static final Logger s_logger = 
Logger.getLogger(DeleteManagementNetworkIpRangeCmd.class);
+
+    public static final String APINAME = "deleteManagementNetworkIpRange";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.POD_ID,
+            type = CommandType.UUID,
+            entityType = PodResponse.class,
+            required = true,
+            description = "UUID of POD, where the IP range belongs to.",
+            validations = ApiArgValidator.PositiveNumber)
+    private Long podId;
+
+    @Parameter(name = ApiConstants.START_IP,
+            type = CommandType.STRING,
+            required = true,
+            description = "The starting IP address.",
+            validations = ApiArgValidator.NotNullOrEmpty)
+    private String startIp;
+
+    @Parameter(name = ApiConstants.END_IP,
+            type = CommandType.STRING,
+            required = true,
+            description = "The ending IP address.",
+            validations = ApiArgValidator.NotNullOrEmpty)
+    private String endIp;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getPodId() {
+        return podId;
+    }
+
+    public String getStartIp() {
+        return startIp;
+    }
+
+    public String getEndIp() {
+        return endIp;
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_MANAGEMENT_IP_RANGE_DELETE;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Deleting management ip range from " + getStartIp() + " to " + 
getEndIp() + " of Pod: " + getPodId();
+    }
+
+    @Override
+    public void execute() {
+        try {
+            _configService.deletePodIpRange(this);
+            SuccessResponse response = new SuccessResponse(getCommandName());
+            this.setResponseObject(response);
+        } catch (ResourceUnavailableException ex) {
+            s_logger.warn("Exception: ", ex);
+            throw new 
ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
+        } catch (ConcurrentOperationException ex) {
+            s_logger.warn("Exception: ", ex);
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
ex.getMessage());
+        } catch (Exception e) {
+            s_logger.warn("Failed to delete management ip range from " + 
getStartIp() + " to " + getEndIp() + " of Pod: " + getPodId(), e);
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseAsyncCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+}
\ No newline at end of file
diff --git a/api/src/org/apache/cloudstack/api/response/PodResponse.java 
b/api/src/org/apache/cloudstack/api/response/PodResponse.java
index 7ff0bfc8bcb..c5c700ef5df 100644
--- a/api/src/org/apache/cloudstack/api/response/PodResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/PodResponse.java
@@ -55,11 +55,11 @@
 
     @SerializedName("startip")
     @Param(description = "the starting IP for the Pod")
-    private String startIp;
+    private List<String> startIp;
 
     @SerializedName("endip")
     @Param(description = "the ending IP for the Pod")
-    private String endIp;
+    private List<String> endIp;
 
     @SerializedName("allocationstate")
     @Param(description = "the allocation state of the Pod")
@@ -117,19 +117,19 @@ public void setNetmask(String netmask) {
         this.netmask = netmask;
     }
 
-    public String getStartIp() {
+    public List<String> getStartIp() {
         return startIp;
     }
 
-    public void setStartIp(String startIp) {
+    public void setStartIp(List<String> startIp) {
         this.startIp = startIp;
     }
 
-    public String getEndIp() {
+    public List<String> getEndIp() {
         return endIp;
     }
 
-    public void setEndIp(String endIp) {
+    public void setEndIp(List<String> endIp) {
         this.endIp = endIp;
     }
 
diff --git a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java 
b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java
index 3b988dac21f..9929cc3e4b2 100644
--- a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java
+++ b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java
@@ -23,17 +23,17 @@
 
 public interface DataCenterIpAddressDao extends 
GenericDao<DataCenterIpAddressVO, Long> {
 
-    public DataCenterIpAddressVO takeIpAddress(long dcId, long podId, long 
instanceId, String reservationId);
+    DataCenterIpAddressVO takeIpAddress(long dcId, long podId, long 
instanceId, String reservationId);
 
-    public DataCenterIpAddressVO takeDataCenterIpAddress(long dcId, String 
reservationId);
+    DataCenterIpAddressVO takeDataCenterIpAddress(long dcId, String 
reservationId);
 
-    public void addIpRange(long dcId, long podId, String start, String end);
+    void addIpRange(long dcId, long podId, String start, String end);
 
-    public void releaseIpAddress(String ipAddress, long dcId, Long instanceId);
+    void releaseIpAddress(String ipAddress, long dcId, Long instanceId);
 
-    public void releaseIpAddress(long nicId, String reservationId);
+    void releaseIpAddress(long nicId, String reservationId);
 
-    public void releaseIpAddress(long nicId);
+    void releaseIpAddress(long nicId);
 
     boolean mark(long dcId, long podId, String ip);
 
@@ -45,8 +45,11 @@
 
     int countIPs(long dcId, boolean onlyCountAllocated);
 
+    int countIpAddressUsage(final String ipAddress, final long podId, final 
long dcId, final boolean onlyCountAllocated);
+
     boolean deleteIpAddressByPod(long podId);
 
     void releasePodIpAddress(long id);
 
+    boolean deleteIpAddressByPodDc(String ipAddress, long podId, long dcId);
 }
diff --git a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java 
b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java
index e840a4d91b0..12bfdc85628 100644
--- a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java
+++ b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java
@@ -97,6 +97,16 @@ public boolean deleteIpAddressByPod(long podId) {
         return remove(sc) > 0;
     }
 
+    @Override
+    public boolean deleteIpAddressByPodDc(String ipAddress, long podId, long 
dcId) {
+        SearchCriteria<DataCenterIpAddressVO> sc = AllFieldsSearch.create();
+        sc.setParameters("ipAddress", ipAddress);
+        sc.setParameters("pod", podId);
+        sc.setParameters("dc", dcId);
+
+        return remove(sc) > 0;
+    }
+
     @Override
     public boolean mark(long dcId, long podId, String ip) {
         SearchCriteria<DataCenterIpAddressVO> sc = AllFieldsSearch.create();
@@ -248,6 +258,22 @@ public int countIPs(long dcId, boolean onlyCountAllocated) 
{
         return count.get(0);
     }
 
+    @Override
+    public int countIpAddressUsage(final String ipAddress, final long podId, 
final long dcId, final boolean onlyCountAllocated) {
+        SearchCriteria<DataCenterIpAddressVO> sc = createSearchCriteria();
+
+        if(onlyCountAllocated) {
+            sc.addAnd("takenAt", SearchCriteria.Op.NNULL);
+        }
+
+        sc.addAnd("ipAddress", SearchCriteria.Op.EQ, ipAddress);
+        sc.addAnd("podId", SearchCriteria.Op.EQ, podId);
+        sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, dcId);
+
+        List<DataCenterIpAddressVO> result = listBy(sc);
+        return result.size();
+    }
+
     public DataCenterIpAddressDaoImpl() {
         super();
 
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java 
b/server/src/com/cloud/api/ApiResponseHelper.java
index 3325be1069c..b0898baa2da 100644
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -945,10 +945,18 @@ public GlobalLoadBalancerResponse 
createGlobalLoadBalancerResponse(GlobalLoadBal
     @Override
     public PodResponse createPodResponse(Pod pod, Boolean showCapacities) {
         String[] ipRange = new String[2];
+        List<String> startIp = new ArrayList<String>();
+        List<String> endIp = new ArrayList<String>();
+
         if (pod.getDescription() != null && pod.getDescription().length() > 0) 
{
-            ipRange = pod.getDescription().split("-");
-        } else {
-            ipRange[0] = pod.getDescription();
+            final String[] existingPodIpRanges = 
pod.getDescription().split(",");
+
+            for(String podIpRange: existingPodIpRanges) {
+                final String[] existingPodIpRange = podIpRange.split("-");
+
+                startIp.add(((existingPodIpRange.length > 0) && 
(existingPodIpRange[0] != null)) ? existingPodIpRange[0] : "");
+                endIp.add(((existingPodIpRange.length > 1) && 
(existingPodIpRange[1] != null)) ? existingPodIpRange[1] : "");
+            }
         }
 
         PodResponse podResponse = new PodResponse();
@@ -960,8 +968,8 @@ public PodResponse createPodResponse(Pod pod, Boolean 
showCapacities) {
             podResponse.setZoneName(zone.getName());
         }
         podResponse.setNetmask(NetUtils.getCidrNetmask(pod.getCidrSize()));
-        podResponse.setStartIp(ipRange[0]);
-        podResponse.setEndIp(((ipRange.length > 1) && (ipRange[1] != null)) ? 
ipRange[1] : "");
+        podResponse.setStartIp(startIp);
+        podResponse.setEndIp(endIp);
         podResponse.setGateway(pod.getGateway());
         podResponse.setAllocationState(pod.getAllocationState().toString());
         if (showCapacities != null && showCapacities) {
diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java 
b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
index 6dab97ae1ed..e99d049b3dd 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -41,7 +41,9 @@
 import org.apache.cloudstack.affinity.AffinityGroupService;
 import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
 import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
+import 
org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
 import 
org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
+import 
org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd;
 import 
org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
 import 
org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd;
@@ -224,6 +226,7 @@
 import com.cloud.vm.dao.VMInstanceDao;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 
 public class ConfigurationManagerImpl extends ManagerBase implements 
ConfigurationManager, ConfigurationService, Configurable {
     public static final Logger s_logger = 
Logger.getLogger(ConfigurationManagerImpl.class);
@@ -997,7 +1000,9 @@ private void checkPodAttributes(final long podId, final 
String podName, final lo
         checkIpRange(startIp, endIp, cidrAddress, cidrSize);
 
         // Check if the IP range overlaps with the public ip
-        checkOverlapPublicIpRange(zoneId, startIp, endIp);
+        if(!Strings.isNullOrEmpty(startIp)) {
+            checkOverlapPublicIpRange(zoneId, startIp, endIp);
+        }
 
         // Check if the gateway is a valid IP address
         if (!NetUtils.isValidIp(gateway)) {
@@ -1090,9 +1095,241 @@ public void doInTransactionWithoutResult(final 
TransactionStatus status) {
         return true;
     }
 
+    @Override
+    @DB
+    public Pod createPodIpRange(final CreateManagementNetworkIpRangeCmd cmd) {
+
+        final Account account = CallContext.current().getCallingAccount();
+
+        if(!_accountMgr.isRootAdmin(account.getId())) {
+            throw new PermissionDeniedException("Cannot perform this 
operation, Calling account is not root admin: " + account.getId());
+        }
+
+        final long podId = cmd.getPodId();
+        final String gateway = cmd.getGateWay();
+        final String netmask = cmd.getNetmask();
+        final String startIp = cmd.getStartIp();
+        String endIp = cmd.getEndIp();
+
+        final HostPodVO pod = _podDao.findById(podId);
+
+        if(pod == null) {
+            throw new InvalidParameterValueException("Unable to find pod by 
ID: " + podId);
+        }
+
+        final long zoneId = pod.getDataCenterId();
+
+        if(!NetUtils.isValidIp(gateway)) {
+            throw new InvalidParameterValueException("The gateway IP address 
is invalid.");
+        }
+
+        if(!NetUtils.isValidNetmask(netmask)) {
+            throw new InvalidParameterValueException("The netmask IP address 
is invalid.");
+        }
+
+        if(endIp == null) {
+            endIp = startIp;
+        }
+
+        final String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask);
+
+        if(!NetUtils.isValidCIDR(cidr)) {
+            throw new InvalidParameterValueException("The CIDR is invalid " + 
cidr);
+        }
+
+        final String cidrAddress = pod.getCidrAddress();
+        final long cidrSize = pod.getCidrSize();
+
+        // Because each pod has only one Gateway and Netmask.
+        if (!gateway.equals(pod.getGateway())) {
+            throw new InvalidParameterValueException("Multiple gateways for 
the POD: " + pod.getId() + " are not allowed. The Gateway should be same as the 
existing Gateway " + pod.getGateway());
+        }
+
+        if (!netmask.equals(NetUtils.getCidrNetmask(cidrSize))) {
+            throw new InvalidParameterValueException("Multiple subnets for the 
POD: " + pod.getId() + " are not allowed. The Netmask should be same as the 
existing Netmask " + NetUtils.getCidrNetmask(cidrSize));
+        }
+
+        // Check if the IP range is valid.
+        checkIpRange(startIp, endIp, cidrAddress, cidrSize);
+
+        // Check if the IP range overlaps with the public ip.
+        checkOverlapPublicIpRange(zoneId, startIp, endIp);
+
+        // Check if the gateway is in the CIDR subnet
+        if (!NetUtils.getCidrSubNet(gateway, 
cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
+            throw new InvalidParameterValueException("The gateway is not in 
the CIDR subnet.");
+        }
+
+        if (NetUtils.ipRangesOverlap(startIp, endIp, gateway, gateway)) {
+            throw new InvalidParameterValueException("The gateway shouldn't 
overlap start/end ip addresses");
+        }
+
+        final String[] existingPodIpRanges = pod.getDescription().split(",");
+
+        for(String podIpRange: existingPodIpRanges) {
+            final String[] existingPodIpRange = podIpRange.split("-");
+
+            if (existingPodIpRange.length > 1) {
+                if (!NetUtils.isValidIp(existingPodIpRange[0]) || 
!NetUtils.isValidIp(existingPodIpRange[1])) {
+                    continue;
+                }
+                // Check if the range overlaps with any existing range.
+                if (NetUtils.ipRangesOverlap(startIp, endIp, 
existingPodIpRange[0], existingPodIpRange[1])) {
+                    throw new InvalidParameterValueException("The new range 
overlaps with existing range. Please add a mutually exclusive range.");
+                }
+            }
+        }
+
+        try {
+            final String endIpFinal = endIp;
+
+            Transaction.execute(new TransactionCallbackNoReturn() {
+                @Override
+                public void doInTransactionWithoutResult(final 
TransactionStatus status) {
+                    String ipRange = pod.getDescription();
+
+                    if(ipRange != null && !ipRange.isEmpty())
+                        ipRange += ("," + startIp + "-" + endIpFinal);
+                    else
+                        ipRange = (startIp + "-" + endIpFinal);
+
+                    pod.setDescription(ipRange);
+
+                    HostPodVO lock = null;
+                    try {
+                        lock = _podDao.acquireInLockTable(podId);
+
+                        if (lock == null) {
+                            String msg = "Unable to acquire lock on table to 
update the ip range of POD: " + pod.getName() + ", Creation failed.";
+                            s_logger.warn(msg);
+                            throw new CloudRuntimeException(msg);
+                        }
+
+                        _podDao.update(podId, pod);
+                    } finally {
+                        if (lock != null) {
+                            _podDao.releaseFromLockTable(podId);
+                        }
+                    }
+
+                    _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, 
endIpFinal);
+                }
+            });
+        } catch (final Exception e) {
+            s_logger.error("Unable to create Pod IP range due to " + 
e.getMessage(), e);
+            throw new CloudRuntimeException("Failed to create Pod IP range. 
Please contact Cloud Support.");
+        }
+
+        return pod;
+    }
+
+    @Override
+    @DB
+    public void deletePodIpRange(final DeleteManagementNetworkIpRangeCmd cmd) 
throws ResourceUnavailableException, ConcurrentOperationException {
+        final long podId = cmd.getPodId();
+        final String startIp = cmd.getStartIp();
+        final String endIp = cmd.getEndIp();
+
+        final HostPodVO pod = _podDao.findById(podId);
+
+        if(pod == null) {
+            throw new InvalidParameterValueException("Unable to find pod by id 
" + podId);
+        }
+
+        if (startIp == null || !NetUtils.isValidIp(startIp)) {
+            throw new InvalidParameterValueException("The start address of the 
IP range is not a valid IP address.");
+        }
+
+        if (endIp == null || !NetUtils.isValidIp(endIp)) {
+            throw new InvalidParameterValueException("The end address of the 
IP range is not a valid IP address.");
+        }
+
+        if (NetUtils.ip2Long(startIp) > NetUtils.ip2Long(endIp)) {
+            throw new InvalidParameterValueException("The start IP address 
must have a lower value than the end IP address.");
+        }
+
+        for(long ipAddr = NetUtils.ip2Long(startIp); ipAddr <= 
NetUtils.ip2Long(endIp); ipAddr++) {
+            
if(_privateIpAddressDao.countIpAddressUsage(NetUtils.long2Ip(ipAddr), podId, 
pod.getDataCenterId(), true) > 0) {
+                throw new CloudRuntimeException("Some IPs of the range has 
been allocated, so it cannot be deleted.");
+            }
+        }
+
+        final String[] existingPodIpRanges = pod.getDescription().split(",");
+
+        if(existingPodIpRanges.length == 0) {
+            throw new InvalidParameterValueException("The IP range cannot be 
found. As the existing IP range is empty.");
+        }
+
+        final String[] newPodIpRanges = new 
String[existingPodIpRanges.length-1];
+        int index = existingPodIpRanges.length-2;
+        boolean foundRange = false;
+
+        for(String podIpRange: existingPodIpRanges) {
+            final String[] existingPodIpRange = podIpRange.split("-");
+
+            if(existingPodIpRange.length > 1) {
+                if (startIp.equals(existingPodIpRange[0]) && 
endIp.equals(existingPodIpRange[1])) {
+                    foundRange = true;
+                } else if (index >= 0) {
+                    newPodIpRanges[index--] = (existingPodIpRange[0] + "-" + 
existingPodIpRange[1]);
+                }
+            }
+        }
+
+        if(!foundRange) {
+            throw new InvalidParameterValueException("The input IP range: " + 
startIp + "-" + endIp + " of pod: " + podId + "is not present. Please input an 
existing range.");
+        }
+
+        final StringBuilder newPodIpRange = new StringBuilder();
+        boolean first = true;
+        for (String podIpRange : newPodIpRanges) {
+            if (first)
+                first = false;
+            else
+                newPodIpRange.append(",");
+
+            newPodIpRange.append(podIpRange);
+        }
+
+        try {
+            Transaction.execute(new TransactionCallbackNoReturn() {
+                @Override
+                public void doInTransactionWithoutResult(final 
TransactionStatus status) {
+                    pod.setDescription(newPodIpRange.toString());
+
+                    HostPodVO lock = null;
+                    try {
+                        lock = _podDao.acquireInLockTable(podId);
+
+                        if (lock == null) {
+                            String msg = "Unable to acquire lock on table to 
update the ip range of POD: " + pod.getName() + ", Deletion failed.";
+                            s_logger.warn(msg);
+                            throw new CloudRuntimeException(msg);
+                        }
+
+                        _podDao.update(podId, pod);
+                    } finally {
+                        if (lock != null) {
+                            _podDao.releaseFromLockTable(podId);
+                        }
+                    }
+
+                    for(long ipAddr = NetUtils.ip2Long(startIp); ipAddr <= 
NetUtils.ip2Long(endIp); ipAddr++) {
+                        if 
(!_privateIpAddressDao.deleteIpAddressByPodDc(NetUtils.long2Ip(ipAddr), podId, 
pod.getDataCenterId())) {
+                            throw new CloudRuntimeException("Failed to cleanup 
private ip address: " + NetUtils.long2Ip(ipAddr) + " of Pod: " + podId + " DC: 
" + pod.getDataCenterId());
+                        }
+                    }
+                }
+            });
+        } catch (final Exception e) {
+            s_logger.error("Unable to delete Pod " + podId + "IP range due to 
" + e.getMessage(), e);
+            throw new CloudRuntimeException("Failed to delete Pod " + podId + 
"IP range. Please contact Cloud Support.");
+        }
+    }
+
     @Override
     public Pod editPod(final UpdatePodCmd cmd) {
-        return editPod(cmd.getId(), cmd.getPodName(), cmd.getStartIp(), 
cmd.getEndIp(), cmd.getGateway(), cmd.getNetmask(), cmd.getAllocationState());
+        return editPod(cmd.getId(), cmd.getPodName(), null, null, 
cmd.getGateway(), cmd.getNetmask(), cmd.getAllocationState());
     }
 
     @Override
@@ -1106,16 +1343,11 @@ public Pod editPod(final long id, String name, String 
startIp, String endIp, Str
             throw new InvalidParameterValueException("Unable to find pod by id 
" + id);
         }
 
-        final String[] existingPodIpRange = pod.getDescription().split("-");
-        String[] leftRangeToAdd = null;
-        String[] rightRangeToAdd = null;
-        boolean allowToDownsize = false;
-
         // If the gateway, CIDR, private IP range is being changed, check if 
the
         // pod has allocated private IP addresses
         if (podHasAllocatedPrivateIPs(id)) {
 
-            if (netmask != null) {
+            if (!Strings.isNullOrEmpty(netmask)) {
                 final long newCidr = NetUtils.getCidrSize(netmask);
                 final long oldCidr = pod.getCidrSize();
 
@@ -1123,31 +1355,6 @@ public Pod editPod(final long id, String name, String 
startIp, String endIp, Str
                     throw new CloudRuntimeException("The specified pod has 
allocated private IP addresses, so its IP address range can be extended only");
                 }
             }
-
-            if (startIp != null && !startIp.equals(existingPodIpRange[0])) {
-                if (NetUtils.ipRangesOverlap(startIp, null, 
existingPodIpRange[0], existingPodIpRange[1])) {
-                    throw new CloudRuntimeException("The specified pod has 
allocated private IP addresses, so its IP address range can be extended only");
-                } else {
-                    leftRangeToAdd = new String[2];
-                    final long endIpForUpdate = 
NetUtils.ip2Long(existingPodIpRange[0]) - 1;
-                    leftRangeToAdd[0] = startIp;
-                    leftRangeToAdd[1] = NetUtils.long2Ip(endIpForUpdate);
-                }
-            }
-
-            if (endIp != null && !endIp.equals(existingPodIpRange[1])) {
-                if (NetUtils.ipRangesOverlap(endIp, endIp, 
existingPodIpRange[0], existingPodIpRange[1])) {
-                    throw new CloudRuntimeException("The specified pod has 
allocated private IP addresses, so its IP address range can be extended only");
-                } else {
-                    rightRangeToAdd = new String[2];
-                    final long startIpForUpdate = 
NetUtils.ip2Long(existingPodIpRange[1]) + 1;
-                    rightRangeToAdd[0] = NetUtils.long2Ip(startIpForUpdate);
-                    rightRangeToAdd[1] = endIp;
-                }
-            }
-
-        } else {
-            allowToDownsize = true;
         }
 
         if (gateway == null) {
@@ -1163,18 +1370,6 @@ public Pod editPod(final long id, String name, String 
startIp, String endIp, Str
             name = oldPodName;
         }
 
-        if (gateway == null) {
-            gateway = pod.getGateway();
-        }
-
-        if (startIp == null) {
-            startIp = existingPodIpRange[0];
-        }
-
-        if (endIp == null) {
-            endIp = existingPodIpRange[1];
-        }
-
         if (allocationStateStr == null) {
             allocationStateStr = pod.getAllocationState().toString();
         }
@@ -1182,17 +1377,39 @@ public Pod editPod(final long id, String name, String 
startIp, String endIp, Str
         // Verify pod's attributes
         final String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask);
         final boolean checkForDuplicates = !oldPodName.equals(name);
-        checkPodAttributes(id, name, pod.getDataCenterId(), gateway, cidr, 
startIp, endIp, allocationStateStr, checkForDuplicates, false);
+        checkPodAttributes(id, name, pod.getDataCenterId(), gateway, cidr, 
startIp, endIp, allocationStateStr, checkForDuplicates, true);
 
-        try {
+        // Valid check is already done in checkPodAttributes method.
+        final String cidrAddress = getCidrAddress(cidr);
+        final long cidrSize = getCidrSize(cidr);
+
+        // Check if start IP and end IP of all the ranges lie in the CIDR 
subnet.
+        final String[] existingPodIpRanges = pod.getDescription().split(",");
+
+        for(String podIpRange: existingPodIpRanges) {
+            final String[] existingPodIpRange = podIpRange.split("-");
+
+            if (existingPodIpRange.length > 1) {
+                if (!NetUtils.isValidIp(existingPodIpRange[0]) || 
!NetUtils.isValidIp(existingPodIpRange[1])) {
+                    continue;
+                }
 
-            final String[] existingPodIpRangeFinal = existingPodIpRange;
-            final String[] leftRangeToAddFinal = leftRangeToAdd;
-            final String[] rightRangeToAddFinal = rightRangeToAdd;
-            final boolean allowToDownsizeFinal = allowToDownsize;
+                if (!NetUtils.getCidrSubNet(existingPodIpRange[0], 
cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
+                    throw new InvalidParameterValueException("The start 
address of the some IP range is not in the CIDR subnet.");
+                }
+
+                if (!NetUtils.getCidrSubNet(existingPodIpRange[1], 
cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
+                    throw new InvalidParameterValueException("The end address 
of the some IP range is not in the CIDR subnet.");
+                }
+
+                if (NetUtils.ipRangesOverlap(existingPodIpRange[0], 
existingPodIpRange[1], gateway, gateway)) {
+                    throw new InvalidParameterValueException("The gateway 
shouldn't overlap some start/end ip addresses");
+                }
+            }
+        }
+
+        try {
             final String allocationStateStrFinal = allocationStateStr;
-            final String startIpFinal = startIp;
-            final String endIpFinal = endIp;
             final String nameFinal = name;
             final String gatewayFinal = gateway;
             Transaction.execute(new TransactionCallbackNoReturn() {
@@ -1200,42 +1417,12 @@ public Pod editPod(final long id, String name, String 
startIp, String endIp, Str
                 public void doInTransactionWithoutResult(final 
TransactionStatus status) {
                     final long zoneId = pod.getDataCenterId();
 
-                    String startIp = startIpFinal;
-                    String endIp = endIpFinal;
-
-                    if (!allowToDownsizeFinal) {
-                        if (leftRangeToAddFinal != null) {
-                            _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), 
leftRangeToAddFinal[0], leftRangeToAddFinal[1]);
-                        }
-
-                        if (rightRangeToAddFinal != null) {
-                            _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), 
rightRangeToAddFinal[0], rightRangeToAddFinal[1]);
-                        }
-
-                    } else {
-                        // delete the old range
-                        _zoneDao.deletePrivateIpAddressByPod(pod.getId());
-
-                        // add the new one
-                        if (startIp == null) {
-                            startIp = existingPodIpRangeFinal[0];
-                        }
-
-                        if (endIp == null) {
-                            endIp = existingPodIpRangeFinal[1];
-                        }
-
-                        _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), 
startIp, endIp);
-                    }
-
                     pod.setName(nameFinal);
                     pod.setDataCenterId(zoneId);
                     pod.setGateway(gatewayFinal);
                     pod.setCidrAddress(getCidrAddress(cidr));
                     pod.setCidrSize(getCidrSize(cidr));
 
-                    final String ipRange = startIp + "-" + endIp;
-                    pod.setDescription(ipRange);
                     Grouping.AllocationState allocationState = null;
                     if (allocationStateStrFinal != null && 
!allocationStateStrFinal.isEmpty()) {
                         allocationState = 
Grouping.AllocationState.valueOf(allocationStateStrFinal);
@@ -1297,7 +1484,7 @@ public HostPodVO createPod(final long userId, final 
String podName, final long z
 
         // endIp is an optional parameter; if not specified - default it to the
         // end ip of the pod's cidr
-        if (startIp != null) {
+        if (!Strings.isNullOrEmpty(startIp)) {
             if (endIp == null) {
                 endIp = NetUtils.getIpRangeEndIpFromCidr(cidrAddress, 
cidrSize);
             }
@@ -1308,7 +1495,7 @@ public HostPodVO createPod(final long userId, final 
String podName, final long z
 
         // Create the new pod in the database
         String ipRange;
-        if (startIp != null) {
+        if (!Strings.isNullOrEmpty(startIp)) {
             ipRange = startIp + "-" + endIp;
         } else {
             throw new InvalidParameterValueException("Start ip is required 
parameter");
@@ -1329,7 +1516,7 @@ public HostPodVO doInTransaction(final TransactionStatus 
status) {
 
                 final HostPodVO pod = _podDao.persist(podFinal);
 
-                if (startIp != null) {
+                if (!Strings.isNullOrEmpty(startIp)) {
                     _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, 
endIpFinal);
                 }
 
@@ -1448,23 +1635,26 @@ private void checkZoneParameters(final String zoneName, 
final String dns1, final
     }
 
     private void checkIpRange(final String startIp, final String endIp, final 
String cidrAddress, final long cidrSize) {
-        if (!NetUtils.isValidIp(startIp)) {
+        //Checking not null for start IP as well. Previously we assumed to be 
not null always.
+        //But the check is required for the change in updatePod API.
+        if (!Strings.isNullOrEmpty(startIp) && !NetUtils.isValidIp(startIp)) {
             throw new InvalidParameterValueException("The start address of the 
IP range is not a valid IP address.");
         }
 
-        if (endIp != null && !NetUtils.isValidIp(endIp)) {
+        if (!Strings.isNullOrEmpty(endIp) && !NetUtils.isValidIp(endIp)) {
             throw new InvalidParameterValueException("The end address of the 
IP range is not a valid IP address.");
         }
 
-        if (!NetUtils.getCidrSubNet(startIp, 
cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
+        //Not null check is required for the change in updatePod API.
+        if (!Strings.isNullOrEmpty(startIp) && 
!NetUtils.getCidrSubNet(startIp, 
cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
             throw new InvalidParameterValueException("The start address of the 
IP range is not in the CIDR subnet.");
         }
 
-        if (endIp != null && !NetUtils.getCidrSubNet(endIp, 
cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
+        if (!Strings.isNullOrEmpty(endIp) && !NetUtils.getCidrSubNet(endIp, 
cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) {
             throw new InvalidParameterValueException("The end address of the 
IP range is not in the CIDR subnet.");
         }
 
-        if (endIp != null && NetUtils.ip2Long(startIp) > 
NetUtils.ip2Long(endIp)) {
+        if (!Strings.isNullOrEmpty(endIp) && NetUtils.ip2Long(startIp) > 
NetUtils.ip2Long(endIp)) {
             throw new InvalidParameterValueException("The start IP address 
must have a lower value than the end IP address.");
         }
 
@@ -1487,15 +1677,20 @@ private void checkOverlapPrivateIpRange(final Long 
zoneId, final String startIp,
 
         final List<HostPodVO> podsInZone = _podDao.listByDataCenterId(zoneId);
         for (final HostPodVO hostPod : podsInZone) {
-            final String[] IpRange = hostPod.getDescription().split("-");
-            if (IpRange[0] == null || IpRange[1] == null) {
-                continue;
-            }
-            if (!NetUtils.isValidIp(IpRange[0]) || 
!NetUtils.isValidIp(IpRange[1])) {
-                continue;
-            }
-            if (NetUtils.ipRangesOverlap(startIp, endIp, IpRange[0], 
IpRange[1])) {
-                throw new InvalidParameterValueException("The Start IP and 
endIP address range overlap with private IP :" + IpRange[0] + ":" + IpRange[1]);
+            final String[] existingPodIpRanges = 
hostPod.getDescription().split(",");
+
+            for(String podIpRange: existingPodIpRanges) {
+                final String[] existingPodIpRange = podIpRange.split("-");
+
+                if (existingPodIpRange.length > 1) {
+                    if (!NetUtils.isValidIp(existingPodIpRange[0]) || 
!NetUtils.isValidIp(existingPodIpRange[1])) {
+                        continue;
+                    }
+
+                    if (NetUtils.ipRangesOverlap(startIp, endIp, 
existingPodIpRange[0], existingPodIpRange[1])) {
+                        throw new InvalidParameterValueException("The Start IP 
and EndIP address range overlap with private IP :" + existingPodIpRange[0] + 
":" + existingPodIpRange[1]);
+                    }
+                }
             }
         }
     }
diff --git a/server/src/com/cloud/network/StorageNetworkManagerImpl.java 
b/server/src/com/cloud/network/StorageNetworkManagerImpl.java
index d72de44885b..020f7b1b044 100644
--- a/server/src/com/cloud/network/StorageNetworkManagerImpl.java
+++ b/server/src/com/cloud/network/StorageNetworkManagerImpl.java
@@ -77,12 +77,21 @@ private void checkOverlapPrivateIpRange(long podId, String 
startIp, String endIp
         if (pod == null) {
             throw new CloudRuntimeException("Cannot find pod " + podId);
         }
-        String[] IpRange = pod.getDescription().split("-");
-        if ((IpRange[0] == null || IpRange[1] == null) || 
(!NetUtils.isValidIp(IpRange[0]) || !NetUtils.isValidIp(IpRange[1]))) {
-            return;
-        }
-        if (NetUtils.ipRangesOverlap(startIp, endIp, IpRange[0], IpRange[1])) {
-            throw new InvalidParameterValueException("The Storage network 
Start IP and endIP address range overlap with private IP :" + IpRange[0] + ":" 
+ IpRange[1]);
+
+        final String[] existingPodIpRanges = pod.getDescription().split(",");
+
+        for(String podIpRange: existingPodIpRanges) {
+            final String[] existingPodIpRange = podIpRange.split("-");
+
+            if (existingPodIpRange.length > 1) {
+                if (!NetUtils.isValidIp(existingPodIpRange[0]) || 
!NetUtils.isValidIp(existingPodIpRange[1])) {
+                    continue;
+                }
+
+                if (NetUtils.ipRangesOverlap(startIp, endIp, 
existingPodIpRange[0], existingPodIpRange[1])) {
+                    throw new InvalidParameterValueException("The Storage 
network Start IP and endIP address range overlap with private IP :" + 
existingPodIpRange[0] + ":" + existingPodIpRange[1]);
+                }
+            }
         }
     }
 
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java 
b/server/src/com/cloud/server/ManagementServerImpl.java
index da987ca1330..230b7755d60 100644
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -105,11 +105,13 @@
 import 
org.apache.cloudstack.api.command.admin.loadbalancer.ListLoadBalancerRuleInstancesCmdByAdmin;
 import org.apache.cloudstack.api.command.admin.network.AddNetworkDeviceCmd;
 import 
org.apache.cloudstack.api.command.admin.network.AddNetworkServiceProviderCmd;
+import 
org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
 import org.apache.cloudstack.api.command.admin.network.CreateNetworkCmdByAdmin;
 import 
org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
 import 
org.apache.cloudstack.api.command.admin.network.CreatePhysicalNetworkCmd;
 import 
org.apache.cloudstack.api.command.admin.network.CreateStorageNetworkIpRangeCmd;
 import 
org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd;
+import 
org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd;
 import org.apache.cloudstack.api.command.admin.network.DeleteNetworkDeviceCmd;
 import 
org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
 import 
org.apache.cloudstack.api.command.admin.network.DeleteNetworkServiceProviderCmd;
@@ -3010,6 +3012,11 @@ public long getMemoryOrCpuCapacityByHost(final Long 
hostId, final short capacity
         cmdList.add(UpdateLBHealthCheckPolicyCmd.class);
         cmdList.add(GetUploadParamsForTemplateCmd.class);
         cmdList.add(GetUploadParamsForVolumeCmd.class);
+        cmdList.add(AcquirePodIpCmdByAdmin.class);
+        cmdList.add(ReleasePodIpCmdByAdmin.class);
+        cmdList.add(CreateManagementNetworkIpRangeCmd.class);
+        cmdList.add(DeleteManagementNetworkIpRangeCmd.class);
+
         // Out-of-band management APIs for admins
         cmdList.add(EnableOutOfBandManagementForHostCmd.class);
         cmdList.add(DisableOutOfBandManagementForHostCmd.class);
@@ -3022,9 +3029,6 @@ public long getMemoryOrCpuCapacityByHost(final Long 
hostId, final short capacity
         cmdList.add(ChangeOutOfBandManagementPasswordCmd.class);
         cmdList.add(GetUserKeysCmd.class);
 
-        cmdList.add(AcquirePodIpCmdByAdmin.class);
-        cmdList.add(ReleasePodIpCmdByAdmin.class);
-
         return cmdList;
     }
 
diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java 
b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
index e9e2b322511..a624986c446 100644
--- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
+++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
@@ -24,7 +24,9 @@
 import javax.naming.ConfigurationException;
 
 import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
+import 
org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
 import 
org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
+import 
org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd;
 import 
org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
 import 
org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd;
 import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd;
@@ -159,6 +161,24 @@ public Pod createPod(long zoneId, String name, String 
startIp, String endIp, Str
         return null;
     }
 
+    /* (non-Javadoc)
+     * @see 
com.cloud.configuration.ConfigurationService#createPodIpRange(org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd)
+     */
+    @Override
+    public Pod createPodIpRange(CreateManagementNetworkIpRangeCmd cmd) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see 
com.cloud.configuration.ConfigurationService#deletePodIpRange(org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd)
+     */
+    @Override
+    public void deletePodIpRange(DeleteManagementNetworkIpRangeCmd cmd) throws 
ResourceUnavailableException, ConcurrentOperationException {
+        // TODO Auto-generated method stub
+        return;
+    }
+
     /* (non-Javadoc)
      * @see 
com.cloud.configuration.ConfigurationService#editPod(org.apache.cloudstack.api.commands.UpdatePodCmd)
      */
diff --git a/ui/l10n/en.js b/ui/l10n/en.js
index 3d0c42147c7..2481d5ab556 100644
--- a/ui/l10n/en.js
+++ b/ui/l10n/en.js
@@ -343,6 +343,7 @@ var dictionary = {"ICMP.code":"ICMP Code",
 "label.add.ldap.account":"Add LDAP account",
 "label.add.list.name":"ACL List Name",
 "label.add.load.balancer":"Add Load Balancer",
+"label.add.management.ip.range":"Add Management IP Range",
 "label.add.more":"Add More",
 "label.add.netScaler.device":"Add Netscaler device",
 "label.add.network":"Add Network",
@@ -1400,6 +1401,7 @@ var dictionary = {"ICMP.code":"ICMP Code",
 "label.remove.ingress.rule":"Remove ingress rule",
 "label.remove.ip.range":"Remove IP range",
 "label.remove.ldap":"Remove LDAP",
+"label.remove.management.ip.range":"Remove Management IP Range",
 "label.remove.network.offering":"Remove network offering",
 "label.remove.pf":"Remove port forwarding rule",
 "label.remove.project.account":"Remove account from project",
diff --git a/ui/scripts/system.js b/ui/scripts/system.js
index fc9b4b8626a..6a314635ed2 100755
--- a/ui/scripts/system.js
+++ b/ui/scripts/system.js
@@ -936,56 +936,154 @@
                                     });
                                 }
                             },
+
                             ipAddresses: {
-                                //read-only listView (no actions) filled with 
pod info (not VlanIpRange info)
                                 title: 'label.ip.ranges',
-                                listView: {
-                                    fields: {
-                                        name: {
-                                            label: 'label.pod'
-                                        },
-                                        //pod name
-                                        gateway: {
-                                            label: 'label.gateway'
-                                        },
-                                        //'Reserved system gateway' is too 
long and causes a visual format bug (2 lines overlay)
-                                        netmask: {
-                                            label: 'label.netmask'
+                                custom: function (args) {
+                                    return $('<div></div>').multiEdit({
+                                        context: args.context,
+                                        noSelect: true,
+                                        fields: {
+                                            'podid': {
+                                                label: 'label.pod',
+                                                select: function (args) {
+                                                    $.ajax({
+                                                        url: 
createURL("listPods&zoneid=" + selectedZoneObj.id),
+                                                        dataType: "json",
+                                                        success: function 
(json) {
+                                                            var items =[];
+                                                            var pods = 
json.listpodsresponse.pod;
+                                                            
$(pods).each(function () {
+                                                                items.push({
+                                                                    name: 
this.id,
+                                                                    
description: this.name
+                                                                });
+                                                            });
+                                                            
args.response.success({
+                                                                data: items
+                                                            });
+                                                        }
+                                                    });
+                                                }
+                                            },
+                                            'gateway': {
+                                                edit: true,
+                                                label: 'label.gateway'
+                                            },
+                                            'netmask': {
+                                                edit: true,
+                                                label: 'label.netmask'
+                                            },
+                                            'startip': {
+                                                edit: true,
+                                                label: 'label.start.IP'
+                                            },
+                                            'endip': {
+                                                edit: true,
+                                                label: 'label.end.IP',
+                                                validation: {
+                                                    required: false
+                                                }
+                                            },
+                                            'add-rule': {
+                                                label: 'label.add',
+                                                addButton: true
+                                            }
                                         },
-                                        //'Reserved system netmask' is too 
long and causes a visual format bug (2 lines overlay)
-                                        startip: {
-                                            label: 'label.start.IP'
+                                        add: {
+                                            label: 'label.add',
+                                            action: function (args) {
+                                                var array1 =[];
+
+                                                array1.push("&podid=" + 
args.data.podid);
+                                                array1.push("&gateway=" + 
args.data.gateway);
+                                                array1.push("&netmask=" + 
args.data.netmask);
+                                                array1.push("&startip=" + 
args.data.startip);
+
+                                                if (args.data.endip != null && 
args.data.endip.length > 0)
+                                                    array1.push("&endip=" + 
args.data.endip);
+
+                                                $.ajax({
+                                                    url: 
createURL("createManagementNetworkIpRange" + array1.join("")),
+                                                    dataType: "json",
+                                                    success: function (json) {
+                                                        args.response.success({
+                                                            _custom: {
+                                                                jobId: 
json.createmanagementnetworkiprangeresponse.jobid
+                                                            },
+                                                            notification: {
+                                                                label: 
'label.add.management.ip.range',
+                                                                poll: 
pollAsyncJobResult
+                                                            }
+                                                        });
+                                                    },
+                                                    error: function 
(XMLHttpResponse) {
+                                                        var errorMsg = 
parseXMLHttpResponse(XMLHttpResponse);
+                                                        
args.response.error(errorMsg);
+                                                    }
+                                                });
+                                            }
                                         },
-                                        //'Reserved system start IP' is too 
long and causes a visual format bug (2 lines overlay)
-                                        endip: {
-                                            label: 'label.end.IP'
-                                        }
-                                        //'Reserved system end IP' is too long 
and causes a visual format bug (2 lines overlay)
-                                    },
-                                    dataProvider: function (args) {
-                                        var array1 =[];
-                                        if (args.filterBy != null) {
-                                            if (args.filterBy.search != null 
&& args.filterBy.search.by != null && args.filterBy.search.value != null) {
-                                                switch 
(args.filterBy.search.by) {
-                                                    case "name":
-                                                    if 
(args.filterBy.search.value.length > 0)
-                                                    array1.push("&keyword=" + 
args.filterBy.search.value);
-                                                    break;
+                                        actions: {
+                                            destroy: {
+                                                label: 'label.delete',
+                                                action: function (args) {
+                                                    var array1 =[];
+                                                    array1.push("&podid=" + 
args.context.multiRule[0].podid);
+                                                    array1.push("&startip=" + 
args.context.multiRule[0].startip);
+                                                    array1.push("&endip=" + 
args.context.multiRule[0].endip);
+
+                                                    $.ajax({
+                                                        url: 
createURL('deleteManagementNetworkIpRange' + array1.join("")),
+                                                        dataType: 'json',
+                                                        async: true,
+                                                        success: function 
(json) {
+                                                            
args.response.success({
+                                                                _custom: {
+                                                                    jobId: 
json.deletemanagementnetworkiprangeresponse.jobid
+                                                                },
+                                                                notification: {
+                                                                    label: 
'label.remove.management.ip.range',
+                                                                    poll: 
pollAsyncJobResult
+                                                                }
+                                                            });
+                                                        },
+                                                        error: function 
(XMLHttpResponse) {
+                                                            var errorMsg = 
parseXMLHttpResponse(XMLHttpResponse);
+                                                            
args.response.error(errorMsg);
+                                                        }
+                                                    });
                                                 }
                                             }
+                                        },
+                                        dataProvider: function (args) {
+                                            $.ajax({
+                                                url: 
createURL("listPods&zoneid=" + selectedZoneObj.id),
+                                                dataType: "json",
+                                                async: true,
+                                                success: function (json) {
+                                                    var items =[];
+
+                                                    var pods = 
json.listpodsresponse.pod;
+                                                    $(pods).each(function () {
+                                                        for (var i = 0; i < 
this.startip.length; i++) {
+                                                            items.push({
+                                                                podid: this.id,
+                                                                gateway: 
this.gateway,
+                                                                netmask: 
this.netmask,
+                                                                startip: 
this.startip[i],
+                                                                endip: 
this.endip[i]
+                                                            });
+                                                        }
+                                                    });
+
+                                                    args.response.success({
+                                                        data: items
+                                                    });
+                                                }
+                                            });
                                         }
-                                        $.ajax({
-                                            url: createURL("listPods&zoneid=" 
+ selectedZoneObj.id + "&page=" + args.page + "&pagesize=" + pageSize + 
array1.join("")),
-                                            dataType: "json",
-                                            async: true,
-                                            success: function (json) {
-                                                var items = 
json.listpodsresponse.pod;
-                                                args.response.success({
-                                                    data: items
-                                                });
-                                            }
-                                        });
-                                    }
+                                    });
                                 }
                             }
                         }
@@ -13465,11 +13563,10 @@
                                 label: 'label.edit',
                                 action: function (args) {
                                     var array1 =[];
+
                                     array1.push("&name=" + 
todb(args.data.name));
                                     array1.push("&netmask=" + 
todb(args.data.netmask));
-                                    array1.push("&startIp=" + 
todb(args.data.startip));
-                                    if (args.data.endip != null && 
args.data.endip.length > 0)
-                                    array1.push("&endIp=" + 
todb(args.data.endip));
+
                                     if (args.data.gateway != null && 
args.data.gateway.length > 0)
                                     array1.push("&gateway=" + 
todb(args.data.gateway));
 
@@ -13730,17 +13827,6 @@
                                             required: true
                                         }
                                     },
-                                    startip: {
-                                        label: 'label.start.IP',
-                                        isEditable: true,
-                                        validation: {
-                                            required: true
-                                        }
-                                    },
-                                    endip: {
-                                        label: 'label.end.IP',
-                                        isEditable: true
-                                    },
                                     gateway: {
                                         label: 'label.gateway',
                                         isEditable: true,
@@ -13756,7 +13842,6 @@
                                         label: 'label.allocation.state'
                                     }
                                 }, {
-
                                     isdedicated: {
                                         label: 'label.dedicated'
                                     },
@@ -13766,13 +13851,10 @@
                                 }],
 
                                 dataProvider: function (args) {
-
                                     $.ajax({
                                         url: createURL("listPods&id=" + 
args.context.pods[0].id),
                                         success: function (json) {
                                             var item = 
json.listpodsresponse.pod[0];
-
-
                                             $.ajax({
                                                 url: 
createURL("listDedicatedPods&podid=" + args.context.pods[0].id),
                                                 success: function (json) {


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to