You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2018/01/05 07:49:08 UTC

[cloudstack] branch master updated: CLOUDSTACK-10126: Separate Subnet for SSVM and CPVM (#2368)

This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/master by this push:
     new bf4f1bb  CLOUDSTACK-10126: Separate Subnet for SSVM and CPVM (#2368)
bf4f1bb is described below

commit bf4f1bbb90a7ac05c7d40b1e5667186a646a25f7
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Fri Jan 5 04:49:03 2018 -0300

    CLOUDSTACK-10126: Separate Subnet for SSVM and CPVM (#2368)
    
    This extends work presented on #2048 on which the ability to extend the management range is provided.
    
    Aim
    This PR allows separating the management network subnet on which SSVM and CPVM are from the virtual routers management subnet.
    
    Detailed use case
    PCI compliance requires that network elements are defined as ‘in scope’ or ‘out of scope’, for compliance purposes. The SSVM and CPVM are both in scope as they allow public HTTP or HTTPS connections. The virtual routers have been defined as out of scope as they have been placed entirely in a firewalled network's segment. However, all of the system VM types share management network. As SSVM and CPVM are both in scope this would bring the virtual routers into scope as well, requiring in [...]
    
    Description
    By this feature it is possible to dedicate a created range for SSVM and CPVM (system vms) and provide a VLAN ID for its range.
    
    A new boolean global configuration is added: system.vm.management.ip.reservation.mode.strictness. If enabled, the use of System VMs management IP reservation is strict, preferred if not. Default value is false (preferred).
    
    Strict reservation: System VMs should try to get a private IP from a range marked for system vms. If not available, deployment fails
    Preferred reservation: System VMS will try to get a private IP from a range marked for system vms. If not available, IP for range not marked for system vms is taken.
---
 .../org/apache/cloudstack/api/ApiConstants.java    |  1 +
 .../network/CreateManagementNetworkIpRangeCmd.java | 21 ++++++++
 .../network/DeleteManagementNetworkIpRangeCmd.java | 10 ++++
 .../cloudstack/api/response/PodResponse.java       | 24 +++++++++
 .../resources/META-INF/db/schema-41000to41100.sql  |  7 +++
 .../src/com/cloud/dc/DataCenterIpAddressVO.java    | 14 +++++
 .../schema/src/com/cloud/dc/dao/DataCenterDao.java | 31 +++++++++--
 .../src/com/cloud/dc/dao/DataCenterDaoImpl.java    | 11 ++--
 .../com/cloud/dc/dao/DataCenterIpAddressDao.java   |  4 +-
 .../cloud/dc/dao/DataCenterIpAddressDaoImpl.java   | 37 +++++++++++--
 server/src/com/cloud/api/ApiResponseHelper.java    |  9 ++++
 .../configuration/ConfigurationManagerImpl.java    | 60 +++++++++++++++++++---
 .../com/cloud/network/IpAddressManagerImpl.java    |  2 +-
 .../cloud/network/guru/PodBasedNetworkGuru.java    | 19 ++++---
 .../com/cloud/server/ConfigurationServerImpl.java  |  2 +-
 .../configuration/ConfigurationManagerTest.java    | 20 ++++++++
 ui/scripts/system.js                               | 24 ++++++++-
 ui/scripts/ui/widgets/multiEdit.js                 | 25 ++++++++-
 18 files changed, 288 insertions(+), 33 deletions(-)

diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 41cf7d9..20a44b0 100644
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -125,6 +125,7 @@ public class ApiConstants {
     public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage";
     public static final String FORMAT = "format";
     public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
+    public static final String FOR_SYSTEM_VMS = "forsystemvms";
     public static final String GATEWAY = "gateway";
     public static final String IP6_GATEWAY = "ip6gateway";
     public static final String GROUP = "group";
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
index e5bfc07..f795746 100644
--- a/api/src/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java
@@ -85,6 +85,16 @@ public class CreateManagementNetworkIpRangeCmd extends BaseAsyncCmd {
             description = "The ending IP address.")
     private String endIp;
 
+    @Parameter(name = ApiConstants.FOR_SYSTEM_VMS,
+            type = CommandType.BOOLEAN,
+            description = "Specify if range is dedicated for CPVM and SSVM.")
+    private Boolean forSystemVms;
+
+    @Parameter(name = ApiConstants.VLAN,
+            type = CommandType.STRING,
+            description = "Optional. The vlan id the ip range sits on, default to Null when it is not specificed which means you network is not on any Vlan")
+    private String vlan;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -109,6 +119,17 @@ public class CreateManagementNetworkIpRangeCmd extends BaseAsyncCmd {
         return endIp;
     }
 
+    public Boolean isForSystemVms() {
+        return forSystemVms == null ? Boolean.FALSE : forSystemVms;
+    }
+
+    public String getVlan() {
+        if (vlan == null || vlan.isEmpty()) {
+            vlan = "untagged";
+        }
+        return vlan;
+    }
+
     @Override
     public String getEventType() {
         return EventTypes.EVENT_MANAGEMENT_IP_RANGE_CREATE;
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
index acb9e7a..d648184 100644
--- a/api/src/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java
@@ -72,6 +72,12 @@ public class DeleteManagementNetworkIpRangeCmd extends BaseAsyncCmd {
             validations = ApiArgValidator.NotNullOrEmpty)
     private String endIp;
 
+    @Parameter(name = ApiConstants.VLAN,
+            type = CommandType.STRING,
+            required = true,
+            description = "The vlan id the ip range sits on")
+    private String vlan;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -88,6 +94,10 @@ public class DeleteManagementNetworkIpRangeCmd extends BaseAsyncCmd {
         return endIp;
     }
 
+    public String getVlan() {
+        return vlan;
+    }
+
     @Override
     public String getEventType() {
         return EventTypes.EVENT_MANAGEMENT_IP_RANGE_DELETE;
diff --git a/api/src/org/apache/cloudstack/api/response/PodResponse.java b/api/src/org/apache/cloudstack/api/response/PodResponse.java
index c5c700e..27ebf71 100644
--- a/api/src/org/apache/cloudstack/api/response/PodResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/PodResponse.java
@@ -61,6 +61,14 @@ public class PodResponse extends BaseResponse {
     @Param(description = "the ending IP for the Pod")
     private List<String> endIp;
 
+    @SerializedName("forsystemvms")
+    @Param(description = "indicates if range is dedicated for CPVM and SSVM")
+    private List<String> forSystemVms;
+
+    @SerializedName("vlanid")
+    @Param(description = "indicates Vlan ID for the range")
+    private List<String> vlanId;
+
     @SerializedName("allocationstate")
     @Param(description = "the allocation state of the Pod")
     private String allocationState;
@@ -133,6 +141,22 @@ public class PodResponse extends BaseResponse {
         this.endIp = endIp;
     }
 
+    public void setForSystemVms(List<String> forSystemVms) {
+        this.forSystemVms = forSystemVms;
+    }
+
+    public List<String> getForSystemVms() {
+        return forSystemVms;
+    }
+
+    public List<String> getVlanId() {
+        return vlanId;
+    }
+
+    public void setVlanId(List<String> vlanId) {
+        this.vlanId = vlanId;
+    }
+
     public String getAllocationState() {
         return allocationState;
     }
diff --git a/engine/schema/resources/META-INF/db/schema-41000to41100.sql b/engine/schema/resources/META-INF/db/schema-41000to41100.sql
index 5d51b47..585c7fd 100644
--- a/engine/schema/resources/META-INF/db/schema-41000to41100.sql
+++ b/engine/schema/resources/META-INF/db/schema-41000to41100.sql
@@ -516,3 +516,10 @@ UPDATE `cloud`.`vm_template` SET guest_os_id=99 WHERE id=8;
 
 -- Network External Ids
 ALTER TABLE `cloud`.`networks` ADD `external_id` varchar(255);
+
+-- Separate Subnet for CPVM and SSVM (system vms)
+ALTER TABLE `cloud`.`op_dc_ip_address_alloc`
+ADD COLUMN `forsystemvms` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'Indicates if IP is dedicated for CPVM or SSVM';
+
+ALTER TABLE `cloud`.`op_dc_ip_address_alloc`
+ADD COLUMN `vlan` INT(10) UNSIGNED NULL COMMENT 'Vlan the management network range is on';
diff --git a/engine/schema/src/com/cloud/dc/DataCenterIpAddressVO.java b/engine/schema/src/com/cloud/dc/DataCenterIpAddressVO.java
index 70b4df1..3d68cc3 100644
--- a/engine/schema/src/com/cloud/dc/DataCenterIpAddressVO.java
+++ b/engine/schema/src/com/cloud/dc/DataCenterIpAddressVO.java
@@ -60,6 +60,12 @@ public class DataCenterIpAddressVO implements InternalIdentity {
     @Column(name = "mac_address")
     long macAddress;
 
+    @Column(name = "forsystemvms")
+    private boolean forSystemVms;
+
+    @Column(name = "vlan")
+    private Integer vlan;
+
     protected DataCenterIpAddressVO() {
     }
 
@@ -113,4 +119,12 @@ public class DataCenterIpAddressVO implements InternalIdentity {
     public long getMacAddress() {
         return macAddress;
     }
+
+    public boolean isForSystemVms() {
+        return forSystemVms;
+    }
+
+    public Integer getVlan() {
+        return vlan;
+    }
 }
diff --git a/engine/schema/src/com/cloud/dc/dao/DataCenterDao.java b/engine/schema/src/com/cloud/dc/dao/DataCenterDao.java
index 4fc055e..a6cd59f 100644
--- a/engine/schema/src/com/cloud/dc/dao/DataCenterDao.java
+++ b/engine/schema/src/com/cloud/dc/dao/DataCenterDao.java
@@ -21,10 +21,35 @@ import java.util.List;
 import com.cloud.dc.DataCenterIpAddressVO;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.DataCenterVnetVO;
-import com.cloud.utils.Pair;
 import com.cloud.utils.db.GenericDao;
 
 public interface DataCenterDao extends GenericDao<DataCenterVO, Long> {
+
+    class PrivateAllocationData {
+
+        private String ipAddress;
+        private Long macAddress;
+        private Integer vlan;
+
+        public PrivateAllocationData(final String ipAddress, final Long macAddress, final Integer vlan) {
+            this.ipAddress = ipAddress;
+            this.macAddress = macAddress;
+            this.vlan = vlan;
+        }
+
+        public String getIpAddress() {
+            return ipAddress;
+        }
+
+        public Long getMacAddress() {
+            return macAddress;
+        }
+
+        public Integer getVlan() {
+            return vlan;
+        }
+    }
+
     DataCenterVO findByName(String name);
 
     /**
@@ -35,7 +60,7 @@ public interface DataCenterDao extends GenericDao<DataCenterVO, Long> {
 
     String[] getNextAvailableMacAddressPair(long id, long mask);
 
-    Pair<String, Long> allocatePrivateIpAddress(long id, long podId, long instanceId, String reservationId);
+    PrivateAllocationData allocatePrivateIpAddress(long id, long podId, long instanceId, String reservationId, boolean forSystemVms);
 
     DataCenterIpAddressVO allocatePrivateIpAddress(long id, String reservationId);
 
@@ -57,7 +82,7 @@ public interface DataCenterDao extends GenericDao<DataCenterVO, Long> {
 
     boolean deleteLinkLocalIpAddressByPod(long podId);
 
-    void addPrivateIpAddress(long dcId, long podId, String start, String end);
+    void addPrivateIpAddress(long dcId, long podId, String start, String end, boolean forSystemVms, Integer vlan);
 
     void addLinkLocalIpAddress(long dcId, long podId, String start, String end);
 
diff --git a/engine/schema/src/com/cloud/dc/dao/DataCenterDaoImpl.java b/engine/schema/src/com/cloud/dc/dao/DataCenterDaoImpl.java
index 847a247..385fb40 100644
--- a/engine/schema/src/com/cloud/dc/dao/DataCenterDaoImpl.java
+++ b/engine/schema/src/com/cloud/dc/dao/DataCenterDaoImpl.java
@@ -38,7 +38,6 @@ import com.cloud.network.dao.AccountGuestVlanMapDao;
 import com.cloud.network.dao.AccountGuestVlanMapVO;
 import com.cloud.org.Grouping;
 import com.cloud.utils.NumbersUtil;
-import com.cloud.utils.Pair;
 import com.cloud.utils.db.DB;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.SearchBuilder;
@@ -247,13 +246,13 @@ public class DataCenterDaoImpl extends GenericDaoBase<DataCenterVO, Long> implem
     }
 
     @Override
-    public Pair<String, Long> allocatePrivateIpAddress(long dcId, long podId, long instanceId, String reservationId) {
+    public PrivateAllocationData allocatePrivateIpAddress(long dcId, long podId, long instanceId, String reservationId, boolean forSystemVms) {
         _ipAllocDao.releaseIpAddress(instanceId);
-        DataCenterIpAddressVO vo = _ipAllocDao.takeIpAddress(dcId, podId, instanceId, reservationId);
+        DataCenterIpAddressVO vo = _ipAllocDao.takeIpAddress(dcId, podId, instanceId, reservationId, forSystemVms);
         if (vo == null) {
             return null;
         }
-        return new Pair<String, Long>(vo.getIpAddress(), vo.getMacAddress());
+        return new PrivateAllocationData(vo.getIpAddress(), vo.getMacAddress(), vo.getVlan());
     }
 
     @Override
@@ -287,8 +286,8 @@ public class DataCenterDaoImpl extends GenericDaoBase<DataCenterVO, Long> implem
     }
 
     @Override
-    public void addPrivateIpAddress(long dcId, long podId, String start, String end) {
-        _ipAllocDao.addIpRange(dcId, podId, start, end);
+    public void addPrivateIpAddress(long dcId, long podId, String start, String end, boolean forSystemVms, Integer vlan) {
+        _ipAllocDao.addIpRange(dcId, podId, start, end, forSystemVms, vlan);
     }
 
     @Override
diff --git a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java
index 9929cc3..bb840b5 100644
--- a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java
+++ b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java
@@ -23,11 +23,11 @@ import com.cloud.utils.db.GenericDao;
 
 public interface DataCenterIpAddressDao extends GenericDao<DataCenterIpAddressVO, Long> {
 
-    DataCenterIpAddressVO takeIpAddress(long dcId, long podId, long instanceId, String reservationId);
+    DataCenterIpAddressVO takeIpAddress(long dcId, long podId, long instanceId, String reservationId, boolean forSystemVms);
 
     DataCenterIpAddressVO takeDataCenterIpAddress(long dcId, String reservationId);
 
-    void addIpRange(long dcId, long podId, String start, String end);
+    void addIpRange(long dcId, long podId, String start, String end, boolean forSystemVms, Integer vlan);
 
     void releaseIpAddress(String ipAddress, long dcId, Long instanceId);
 
diff --git a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java
index 12bfdc8..8b0a44b 100644
--- a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java
+++ b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDaoImpl.java
@@ -22,6 +22,8 @@ import java.util.Date;
 import java.util.List;
 
 
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
@@ -38,7 +40,7 @@ import com.cloud.utils.net.NetUtils;
 
 @Component
 @DB
-public class DataCenterIpAddressDaoImpl extends GenericDaoBase<DataCenterIpAddressVO, Long> implements DataCenterIpAddressDao {
+public class DataCenterIpAddressDaoImpl extends GenericDaoBase<DataCenterIpAddressVO, Long> implements DataCenterIpAddressDao, Configurable {
     private static final Logger s_logger = Logger.getLogger(DataCenterIpAddressDaoImpl.class);
 
     private final SearchBuilder<DataCenterIpAddressVO> AllFieldsSearch;
@@ -47,16 +49,26 @@ public class DataCenterIpAddressDaoImpl extends GenericDaoBase<DataCenterIpAddre
     private final GenericSearchBuilder<DataCenterIpAddressVO, Integer> AllAllocatedIpCount;
     private final GenericSearchBuilder<DataCenterIpAddressVO, Integer> AllAllocatedIpCountForDc;
 
+    private static final ConfigKey<Boolean> SystemVmManagementIpReservationModeStrictness = new ConfigKey<Boolean>("Advanced",
+            Boolean.class, "system.vm.management.ip.reservation.mode.strictness", "false","If enabled, the use of System VMs management IP reservation is strict, preferred if not.", false, ConfigKey.Scope.Global);
+
     @Override
     @DB
-    public DataCenterIpAddressVO takeIpAddress(long dcId, long podId, long instanceId, String reservationId) {
+    public DataCenterIpAddressVO takeIpAddress(long dcId, long podId, long instanceId, String reservationId, boolean forSystemVms) {
         SearchCriteria<DataCenterIpAddressVO> sc = AllFieldsSearch.create();
         sc.setParameters("pod", podId);
         sc.setParameters("taken", (Date)null);
+        sc.setParameters("forSystemVms", forSystemVms);
 
         TransactionLegacy txn = TransactionLegacy.currentTxn();
         txn.start();
         DataCenterIpAddressVO vo = lockOneRandomRow(sc, true);
+
+        // If there is no explicitly created range for system vms and reservation mode is preferred (strictness = false)
+        if (forSystemVms && vo == null && !SystemVmManagementIpReservationModeStrictness.value()) {
+            sc.setParameters("forSystemVms", false);
+            vo = lockOneRandomRow(sc, true);
+        }
         if (vo == null) {
             txn.rollback();
             return null;
@@ -121,10 +133,10 @@ public class DataCenterIpAddressDaoImpl extends GenericDaoBase<DataCenterIpAddre
 
     @Override
     @DB
-    public void addIpRange(long dcId, long podId, String start, String end) {
+    public void addIpRange(long dcId, long podId, String start, String end, boolean forSystemVms, Integer vlan) {
         TransactionLegacy txn = TransactionLegacy.currentTxn();
-        String insertSql = "INSERT INTO `cloud`.`op_dc_ip_address_alloc` (ip_address, data_center_id, pod_id, mac_address) " +
-            "VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?))";
+        String insertSql = "INSERT INTO `cloud`.`op_dc_ip_address_alloc` (ip_address, data_center_id, pod_id, mac_address, forsystemvms" + (vlan == null ? ") " : ", vlan) ") +
+                "VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?), ?" + (vlan == null ? ")" : ", ?)");
         String updateSql = "UPDATE `cloud`.`data_center` set mac_address = mac_address+1 where id=?";
 
         long startIP = NetUtils.ip2Long(start);
@@ -139,6 +151,10 @@ public class DataCenterIpAddressDaoImpl extends GenericDaoBase<DataCenterIpAddre
                     insertPstmt.setLong(2, dcId);
                     insertPstmt.setLong(3, podId);
                     insertPstmt.setLong(4, dcId);
+                    insertPstmt.setBoolean(5, forSystemVms);
+                    if (vlan != null) {
+                        insertPstmt.setInt(6, vlan);
+                    }
                     insertPstmt.executeUpdate();
                 }
                 try(PreparedStatement updatePstmt = txn.prepareStatement(updateSql);) {
@@ -285,6 +301,7 @@ public class DataCenterIpAddressDaoImpl extends GenericDaoBase<DataCenterIpAddre
         AllFieldsSearch.and("ipAddress", AllFieldsSearch.entity().getIpAddress(), SearchCriteria.Op.EQ);
         AllFieldsSearch.and("reservation", AllFieldsSearch.entity().getReservationId(), SearchCriteria.Op.EQ);
         AllFieldsSearch.and("taken", AllFieldsSearch.entity().getTakenAt(), SearchCriteria.Op.EQ);
+        AllFieldsSearch.and("forSystemVms", AllFieldsSearch.entity().isForSystemVms(), SearchCriteria.Op.EQ);
         AllFieldsSearch.done();
 
         AllIpCount = createSearchBuilder(Integer.class);
@@ -309,4 +326,14 @@ public class DataCenterIpAddressDaoImpl extends GenericDaoBase<DataCenterIpAddre
         AllAllocatedIpCountForDc.and("removed", AllAllocatedIpCountForDc.entity().getTakenAt(), SearchCriteria.Op.NNULL);
         AllAllocatedIpCountForDc.done();
     }
+
+    @Override
+    public String getConfigComponentName() {
+        return DataCenterIpAddressDao.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey<?>[] {SystemVmManagementIpReservationModeStrictness};
+    }
 }
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index e352198..49b8c15 100644
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -947,6 +947,8 @@ public class ApiResponseHelper implements ResponseGenerator {
         String[] ipRange = new String[2];
         List<String> startIp = new ArrayList<String>();
         List<String> endIp = new ArrayList<String>();
+        List<String> forSystemVms = new ArrayList<String>();
+        List<String> vlanIds = new ArrayList<String>();
 
         if (pod.getDescription() != null && pod.getDescription().length() > 0) {
             final String[] existingPodIpRanges = pod.getDescription().split(",");
@@ -956,6 +958,11 @@ public class ApiResponseHelper implements ResponseGenerator {
 
                 startIp.add(((existingPodIpRange.length > 0) && (existingPodIpRange[0] != null)) ? existingPodIpRange[0] : "");
                 endIp.add(((existingPodIpRange.length > 1) && (existingPodIpRange[1] != null)) ? existingPodIpRange[1] : "");
+                forSystemVms.add((existingPodIpRange.length > 2) && (existingPodIpRange[2] != null) ? existingPodIpRange[2] : "0");
+                vlanIds.add((existingPodIpRange.length > 3) &&
+                        (existingPodIpRange[3] != null && !existingPodIpRange.equals("untagged")) ?
+                        BroadcastDomainType.Vlan.toUri(existingPodIpRange[3]).toString() :
+                        BroadcastDomainType.Vlan.toUri(Vlan.UNTAGGED).toString());
             }
         }
 
@@ -970,6 +977,8 @@ public class ApiResponseHelper implements ResponseGenerator {
         podResponse.setNetmask(NetUtils.getCidrNetmask(pod.getCidrSize()));
         podResponse.setStartIp(startIp);
         podResponse.setEndIp(endIp);
+        podResponse.setForSystemVms(forSystemVms);
+        podResponse.setVlanId(vlanIds);
         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 9cbe305..c3e9e11 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -17,6 +17,7 @@
 package com.cloud.configuration;
 
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.util.ArrayList;
@@ -363,6 +364,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
     public static final ConfigKey<Boolean> SystemVMUseLocalStorage = new ConfigKey<Boolean>(Boolean.class, "system.vm.use.local.storage", "Advanced", "false",
             "Indicates whether to use local storage pools or shared storage pools for system VMs.", false, ConfigKey.Scope.Zone, null);
 
+    private static final String DefaultForSystemVmsForPodIpRange = "0";
+    private static final String DefaultVlanForPodIpRange = Vlan.UNTAGGED.toString();
+
     private static final Set<Provider> VPC_ONLY_PROVIDERS = Sets.newHashSet(Provider.VPCVirtualRouter, Provider.JuniperContrailVpcRouter, Provider.InternalLbVm);
 
     @Override
@@ -1095,6 +1099,25 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         return true;
     }
 
+    /**
+     * Get vlan number from vlan uri
+     * @param vlan
+     * @return
+     */
+    protected String getVlanNumberFromUri(String vlan) {
+        URI uri;
+        try {
+            uri = new URI(vlan);
+            String vlanId = BroadcastDomainType.getValue(uri);
+            if (vlanId == null || !uri.getScheme().equalsIgnoreCase("vlan")) {
+                throw new CloudRuntimeException("Vlan parameter : " + vlan + " is not in valid format");
+            }
+            return vlanId;
+        } catch (URISyntaxException e) {
+            throw new CloudRuntimeException("Invalid vlan parameter: " + vlan + " can't get vlan number from it due to: " + e.getMessage());
+        }
+    }
+
     @Override
     @DB
     public Pod createPodIpRange(final CreateManagementNetworkIpRangeCmd cmd) {
@@ -1110,6 +1133,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         final String netmask = cmd.getNetmask();
         final String startIp = cmd.getStartIp();
         String endIp = cmd.getEndIp();
+        final boolean forSystemVms = cmd.isForSystemVms();
+        String vlan = cmd.getVlan();
+        if (!(Strings.isNullOrEmpty(vlan) || vlan.startsWith(BroadcastDomainType.Vlan.scheme()))) {
+            vlan = BroadcastDomainType.Vlan.toUri(vlan).toString();
+        }
+
+        String vlanNumberFromUri = getVlanNumberFromUri(vlan);
+        final Integer vlanId = vlanNumberFromUri.equals(Vlan.UNTAGGED.toString()) ? null : Integer.parseInt(vlanNumberFromUri);
 
         final HostPodVO pod = _podDao.findById(podId);
 
@@ -1188,10 +1219,15 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                 public void doInTransactionWithoutResult(final TransactionStatus status) {
                     String ipRange = pod.getDescription();
 
+                    /*
+                     * POD Description is refactored to:
+                     * <START_IP>-<END_IP>-<FOR_SYSTEM_VMS>-<VLAN>,<START_IP>-<END_IP>-<FOR_SYSTEM_VMS>-<VLAN>,...
+                    */
+                    String range = startIp + "-" + endIpFinal + "-" + (forSystemVms ? "1" : "0") + "-" + (vlanId == null ? DefaultVlanForPodIpRange : vlanId);
                     if(ipRange != null && !ipRange.isEmpty())
-                        ipRange += ("," + startIp + "-" + endIpFinal);
+                        ipRange += ("," + range);
                     else
-                        ipRange = (startIp + "-" + endIpFinal);
+                        ipRange = (range);
 
                     pod.setDescription(ipRange);
 
@@ -1212,7 +1248,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                         }
                     }
 
-                    _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal);
+                    _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal, forSystemVms, vlanId);
                 }
             });
         } catch (final Exception e) {
@@ -1229,6 +1265,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         final long podId = cmd.getPodId();
         final String startIp = cmd.getStartIp();
         final String endIp = cmd.getEndIp();
+        String vlan = cmd.getVlan();
+        try {
+            vlan = BroadcastDomainType.getValue(vlan);
+        } catch (URISyntaxException e) {
+            throw new CloudRuntimeException("Incorrect vlan " + vlan);
+        }
 
         final HostPodVO pod = _podDao.findById(podId);
 
@@ -1268,10 +1310,13 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
             final String[] existingPodIpRange = podIpRange.split("-");
 
             if(existingPodIpRange.length > 1) {
-                if (startIp.equals(existingPodIpRange[0]) && endIp.equals(existingPodIpRange[1])) {
+                if (startIp.equals(existingPodIpRange[0]) && endIp.equals(existingPodIpRange[1]) &&
+                        (existingPodIpRange.length > 3 ? vlan.equals(existingPodIpRange[3]) : vlan.equals(DefaultVlanForPodIpRange))) {
                     foundRange = true;
                 } else if (index >= 0) {
-                    newPodIpRanges[index--] = (existingPodIpRange[0] + "-" + existingPodIpRange[1]);
+                    newPodIpRanges[index--] = (existingPodIpRange[0] + "-" + existingPodIpRange[1] + "-" +
+                            (existingPodIpRange.length > 2 ? existingPodIpRange[2] : DefaultForSystemVmsForPodIpRange) + "-" +
+                            (existingPodIpRange.length > 3 ? existingPodIpRange[3] : DefaultVlanForPodIpRange));
                 }
             }
         }
@@ -1495,8 +1540,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
         // Create the new pod in the database
         String ipRange;
+
         if (!Strings.isNullOrEmpty(startIp)) {
-            ipRange = startIp + "-" + endIp;
+            ipRange = startIp + "-" + endIp + "-" + DefaultForSystemVmsForPodIpRange + "-" + DefaultVlanForPodIpRange;
         } else {
             throw new InvalidParameterValueException("Start ip is required parameter");
         }
@@ -1517,7 +1563,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                 final HostPodVO pod = _podDao.persist(podFinal);
 
                 if (!Strings.isNullOrEmpty(startIp)) {
-                    _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal);
+                    _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal, false, null);
                 }
 
                 final String[] linkLocalIpRanges = getLinkLocalIPRange();
diff --git a/server/src/com/cloud/network/IpAddressManagerImpl.java b/server/src/com/cloud/network/IpAddressManagerImpl.java
index 5180958..dca994d 100644
--- a/server/src/com/cloud/network/IpAddressManagerImpl.java
+++ b/server/src/com/cloud/network/IpAddressManagerImpl.java
@@ -1065,7 +1065,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
         if (podvo == null)
             throw new ResourceAllocationException("No sush pod exists", ResourceType.network);
 
-        vo = _privateIPAddressDao.takeIpAddress(zone.getId(), podvo.getId(), 0, caller.getId() + "");
+        vo = _privateIPAddressDao.takeIpAddress(zone.getId(), podvo.getId(), 0, caller.getId() + "", false);
         if(vo == null)
             throw new ResourceAllocationException("Unable to allocate IP from this Pod", ResourceType.network);
         if (vo.getIpAddress() == null)
diff --git a/server/src/com/cloud/network/guru/PodBasedNetworkGuru.java b/server/src/com/cloud/network/guru/PodBasedNetworkGuru.java
index e760001..eb80c12 100644
--- a/server/src/com/cloud/network/guru/PodBasedNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/PodBasedNetworkGuru.java
@@ -16,6 +16,8 @@
 // under the License.
 package com.cloud.network.guru;
 
+import com.cloud.dc.dao.DataCenterDao.PrivateAllocationData;
+import com.cloud.vm.VirtualMachine;
 import java.util.Random;
 
 import javax.inject.Inject;
@@ -39,7 +41,6 @@ import com.cloud.network.StorageNetworkManager;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.offering.NetworkOffering;
 import com.cloud.user.Account;
-import com.cloud.utils.Pair;
 import com.cloud.utils.component.AdapterBase;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
@@ -119,19 +120,25 @@ public class PodBasedNetworkGuru extends AdapterBase implements NetworkGuru {
         throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
         Pod pod = dest.getPod();
 
-        Pair<String, Long> ip = _dcDao.allocatePrivateIpAddress(dest.getDataCenter().getId(), dest.getPod().getId(), nic.getId(), context.getReservationId());
-        if (ip == null) {
+        boolean forSystemVms = vm.getType().equals(VirtualMachine.Type.ConsoleProxy) || vm.getType().equals(VirtualMachine.Type.SecondaryStorageVm);
+        PrivateAllocationData result = _dcDao.allocatePrivateIpAddress(dest.getDataCenter().getId(), dest.getPod().getId(), nic.getId(), context.getReservationId(), forSystemVms);
+        if (result == null) {
             throw new InsufficientAddressCapacityException("Unable to get a management ip address", Pod.class, pod.getId());
         }
+        Integer vlan = result.getVlan();
 
-        nic.setIPv4Address(ip.first());
-        nic.setMacAddress(NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ip.second(), NetworkModel.MACIdentifier.value())));
+        nic.setIPv4Address(result.getIpAddress());
+        nic.setMacAddress(NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(result.getMacAddress(), NetworkModel.MACIdentifier.value())));
         nic.setIPv4Gateway(pod.getGateway());
         nic.setFormat(AddressFormat.Ip4);
         String netmask = NetUtils.getCidrNetmask(pod.getCidrSize());
         nic.setIPv4Netmask(netmask);
         nic.setBroadcastType(BroadcastDomainType.Native);
-        nic.setBroadcastUri(null);
+        if (vlan != null) {
+            nic.setBroadcastUri(BroadcastDomainType.Native.toUri(vlan));
+        } else {
+            nic.setBroadcastUri(null);
+        }
         nic.setIsolationUri(null);
 
         s_logger.debug("Allocated a nic " + nic + " for " + vm);
diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java
index 106aa9e..854ec13 100644
--- a/server/src/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/com/cloud/server/ConfigurationServerImpl.java
@@ -902,7 +902,7 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
                     }
 
                     if (startIp != null) {
-                        _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal);
+                        _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal, false, null);
                     }
 
                     String ipNums = _configDao.getValue("linkLocalIp.nums");
diff --git a/server/test/com/cloud/configuration/ConfigurationManagerTest.java b/server/test/com/cloud/configuration/ConfigurationManagerTest.java
index dcb8b45..8648033 100644
--- a/server/test/com/cloud/configuration/ConfigurationManagerTest.java
+++ b/server/test/com/cloud/configuration/ConfigurationManagerTest.java
@@ -891,4 +891,24 @@ public class ConfigurationManagerTest {
         result = configurationMgr.hasSameSubnet(false, null, null, null, null, null, null, true, null, null, "2001:db8:0:f101::2", "2001:db8:0:f101::a", ipV6Network);
         Assert.assertTrue(result);
     }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testGetVlanNumberFromUriInvalidParameter() {
+        configurationMgr.getVlanNumberFromUri("vlan");
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testGetVlanNumberFromUriInvalidSintax() {
+        configurationMgr.getVlanNumberFromUri("xxx://7");
+    }
+
+    @Test
+    public void testGetVlanNumberFromUriVlan() {
+        Assert.assertEquals("7", configurationMgr.getVlanNumberFromUri("vlan://7"));
+    }
+
+    @Test
+    public void testGetVlanNumberFromUriUntagged() {
+        Assert.assertEquals("untagged", configurationMgr.getVlanNumberFromUri("vlan://untagged"));
+    }
 }
diff --git a/ui/scripts/system.js b/ui/scripts/system.js
index d0e6162..f216423 100755
--- a/ui/scripts/system.js
+++ b/ui/scripts/system.js
@@ -974,6 +974,13 @@
                                                 edit: true,
                                                 label: 'label.netmask'
                                             },
+                                            'vlan': {
+                                                edit: true,
+                                                label: 'label.vlan',
+                                                validation: {
+                                                    required: false
+                                                }
+                                            },
                                             'startip': {
                                                 edit: true,
                                                 label: 'label.start.IP'
@@ -985,6 +992,10 @@
                                                     required: false
                                                 }
                                             },
+                                            'systemvms' : {
+                                                isBoolean: true,
+                                                label: 'label.system.vms'
+                                            },
                                             'add-rule': {
                                                 label: 'label.add',
                                                 addButton: true
@@ -1003,6 +1014,13 @@
                                                 if (args.data.endip != null && args.data.endip.length > 0)
                                                     array1.push("&endip=" + args.data.endip);
 
+                                                if (args.data.systemvms) {
+                                                    array1.push("&forsystemvms=" + (args.data.systemvms == "on" ? "true" : "false"));
+                                                }
+
+                                                if (args.data.vlan != null && args.data.vlan.length > 0)
+                                                    array1.push("&vlan=" + todb(args.data.vlan));
+
                                                 $.ajax({
                                                     url: createURL("createManagementNetworkIpRange" + array1.join("")),
                                                     dataType: "json",
@@ -1032,6 +1050,7 @@
                                                     array1.push("&podid=" + args.context.multiRule[0].podid);
                                                     array1.push("&startip=" + args.context.multiRule[0].startip);
                                                     array1.push("&endip=" + args.context.multiRule[0].endip);
+                                                    array1.push("&vlan=" + args.context.multiRule[0].vlan);
 
                                                     $.ajax({
                                                         url: createURL('deleteManagementNetworkIpRange' + array1.join("")),
@@ -1067,12 +1086,15 @@
                                                     var pods = json.listpodsresponse.pod;
                                                     $(pods).each(function () {
                                                         for (var i = 0; i < this.startip.length; i++) {
+                                                            var systemvmsValue = this.forsystemvms[i] == "1" ? true : false;
                                                             items.push({
                                                                 podid: this.id,
                                                                 gateway: this.gateway,
                                                                 netmask: this.netmask,
                                                                 startip: this.startip[i],
-                                                                endip: this.endip[i]
+                                                                endip: this.endip[i],
+                                                                systemvms: systemvmsValue,
+                                                                vlan: this.vlanid[i]
                                                             });
                                                         }
                                                     });
diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js
index 677448a..c3fa97c 100755
--- a/ui/scripts/ui/widgets/multiEdit.js
+++ b/ui/scripts/ui/widgets/multiEdit.js
@@ -179,6 +179,19 @@
                             }
                             $td.attr('title', data[fieldName]);
                         }
+                    } else if (field.isBoolean) {
+                        var $checkbox = $('<input>');
+                        $checkbox.attr({
+                            disabled: true,
+                            name: fieldName,
+                            type: 'checkbox'
+                        });
+                        if (_s(data[fieldName])) {
+                            $checkbox.attr({
+                                checked: true
+                            });
+                        }
+                        $checkbox.appendTo($td);
                     } else if (field.select) {
                         // Get matching option text
                         var $matchingSelect = $multi.find('select')
@@ -980,6 +993,12 @@
                         error: function(args) {}
                     }
                 });
+            } else if (field.isBoolean) {
+                var $input = $('<input>')
+                    .attr({
+                        name: fieldName,
+                        type: 'checkbox'
+                }).appendTo($td);
             } else if (field.edit && field.edit != 'ignore') {
                 if (field.range) {
                     var $range = $('<div>').addClass('range').appendTo($td);
@@ -1118,7 +1137,11 @@
                 $multi.find('input').each(function() {
                     var $input = $(this);
 
-                    if ($input.data('multi-default-value')) {
+                    if ($input.is(":checkbox")) {
+                        $input.attr({
+                            checked: false
+                        });
+                    } else if ($input.data('multi-default-value')) {
                         $input.val($input.data('multi-default-value'));
                     } else {
                         $input.val('');

-- 
To stop receiving notification emails like this one, please contact
['"commits@cloudstack.apache.org" <co...@cloudstack.apache.org>'].