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/06 17:44:35 UTC

[cloudstack] branch master updated: CLOUDSTACK-10109: Enable dedication of public IPs to SSVM and CPVM (#2295)

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 90ef67b  CLOUDSTACK-10109: Enable dedication of public IPs to SSVM and CPVM (#2295)
90ef67b is described below

commit 90ef67bab993c95b253cc810fb0b67fe8d6fc6e8
Author: Nicolas Vazquez <ni...@gmail.com>
AuthorDate: Sat Jan 6 14:44:30 2018 -0300

    CLOUDSTACK-10109: Enable dedication of public IPs to SSVM and CPVM (#2295)
    
    This feature allow admins to dedicate a range of public IP addresses to the SSVM and CPVM, such that they can be subject to specific external firewall rules. The option to dedicate a public IP range to the System VMs (SSVM & CPVM) is added to the createVlanIpRange API method and the UI.
    
    Solution:
    Global setting 'system.vm.public.ip.reservation.mode.strictness' is added to determine if the use of the system VM reservation is strict (when true) or preferred (false), false by default.
    When a range has been dedicated to System VMs, CloudStack should apply IPs from that range to
    the public interfaces of the CPVM and the SSVM depending on global setting's value:
    
    If the global setting is set to false: then CloudStack will use any unused and unreserved public IP
    addresses for system VMs only when the pool of reserved IPs has been exhausted
    If the global setting is set to true: then CloudStack will fail to deploy the system VM when the pool
    of reserved IPs has been exhausted, citing the lack of available IPs.
    UI Changes
    Under Infrastructure -> Zone -> Physical Network -> Public -> IP Ranges, button 'Account' label is refactored to 'Set reservation'.
    
    When that button is clicked, dialog displayed is also refactored, including a new checkbox 'System VMs' which indicates if range should be dedicated for CPVM and SSVM, and a note indicating its usage.
    
    When clicking on button for any created range, UI dialog displayed indicates whether IP range is dedicated for system vms or not.
---
 .../command/admin/vlan/CreateVlanIpRangeCmd.java   |   7 +
 .../api/response/VlanIpRangeResponse.java          |  12 ++
 .../cloud/configuration/ConfigurationManager.java  |   2 +-
 .../src/com/cloud/network/IpAddressManager.java    |   2 +-
 .../resources/META-INF/db/schema-41000to41100.sql  |   5 +-
 .../src/com/cloud/network/dao/IPAddressVO.java     |   7 +
 .../networkservice/BaremetaNetworkGuru.java        |   2 +-
 .../cloud/network/lb/LoadBalanceRuleHandler.java   |   2 +-
 server/src/com/cloud/api/ApiResponseHelper.java    |  20 ++
 .../configuration/ConfigurationManagerImpl.java    |  27 ++-
 .../ExternalLoadBalancerDeviceManagerImpl.java     |   4 +-
 .../com/cloud/network/IpAddressManagerImpl.java    |  39 +++-
 .../src/com/cloud/network/NetworkServiceImpl.java  |   2 +-
 .../network/guru/DirectPodBasedNetworkGuru.java    |   2 +-
 .../com/cloud/network/guru/PublicNetworkGuru.java  |   6 +-
 .../com/cloud/server/ConfigurationServerImpl.java  |   2 +-
 server/src/com/cloud/test/IPRangeConfig.java       |   7 +-
 .../cloud/vpc/MockConfigurationManagerImpl.java    |   2 +-
 test/integration/smoke/test_public_ip_range.py     | 240 +++++++++++++++++++++
 tools/marvin/marvin/lib/base.py                    |   4 +-
 ui/l10n/ar.js                                      |   2 +
 ui/l10n/ca.js                                      |   2 +
 ui/l10n/de_DE.js                                   |   2 +
 ui/l10n/en.js                                      |   2 +
 ui/l10n/es.js                                      |   2 +
 ui/l10n/fr_FR.js                                   |   2 +
 ui/l10n/hu.js                                      |   2 +
 ui/l10n/it_IT.js                                   |   2 +
 ui/l10n/ja_JP.js                                   |   2 +
 ui/l10n/ko_KR.js                                   |   2 +
 ui/l10n/nb_NO.js                                   |   2 +
 ui/l10n/nl_NL.js                                   |   2 +
 ui/l10n/pl.js                                      |   2 +
 ui/l10n/pt_BR.js                                   |   2 +
 ui/l10n/ru_RU.js                                   |   2 +
 ui/l10n/zh_CN.js                                   |   2 +
 ui/scripts/docs.js                                 |   3 +
 ui/scripts/system.js                               |  43 +++-
 38 files changed, 428 insertions(+), 44 deletions(-)

diff --git a/api/src/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java
index a2da7db..fa66fdd 100644
--- a/api/src/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java
@@ -112,6 +112,9 @@ public class CreateVlanIpRangeCmd extends BaseCmd {
     @Parameter(name = ApiConstants.IP6_CIDR, type = CommandType.STRING, description = "the CIDR of IPv6 network, must be at least /64")
     private String ip6Cidr;
 
+    @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if IP range is set to system vms, false if not")
+    private Boolean forSystemVms;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -136,6 +139,10 @@ public class CreateVlanIpRangeCmd extends BaseCmd {
         return gateway;
     }
 
+    public Boolean isForSystemVms() {
+        return forSystemVms == null ? Boolean.FALSE : forSystemVms;
+    }
+
     public String getNetmask() {
         return netmask;
     }
diff --git a/api/src/org/apache/cloudstack/api/response/VlanIpRangeResponse.java b/api/src/org/apache/cloudstack/api/response/VlanIpRangeResponse.java
index 5921484..5656a92 100644
--- a/api/src/org/apache/cloudstack/api/response/VlanIpRangeResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/VlanIpRangeResponse.java
@@ -116,10 +116,22 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit
     @Param(description = "the cidr of IPv6 network")
     private String ip6Cidr;
 
+    @SerializedName(ApiConstants.FOR_SYSTEM_VMS)
+    @Param(description = "indicates whether VLAN IP range is dedicated to system vms or not")
+    private Boolean forSystemVms;
+
     public void setId(String id) {
         this.id = id;
     }
 
+    public Boolean getForSystemVms() {
+        return forSystemVms;
+    }
+
+    public void setForSystemVms(Boolean forSystemVms) {
+        this.forSystemVms = forSystemVms;
+    }
+
     public void setForVirtualNetwork(Boolean forVirtualNetwork) {
         this.forVirtualNetwork = forVirtualNetwork;
     }
diff --git a/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java
index 0c7ed3a..235b241 100644
--- a/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java
+++ b/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java
@@ -213,7 +213,7 @@ public interface ConfigurationManager {
             boolean conserveMode, Map<Service, Map<Capability, String>> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent,
             Map<NetworkOffering.Detail, String> details, boolean egressDefaultPolicy, Integer maxconn, boolean enableKeepAlive, Boolean forVpc);
 
-    Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP,
+    Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, boolean forSystemVms, Long podId, String startIP, String endIP,
         String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr)
         throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException;
 
diff --git a/engine/components-api/src/com/cloud/network/IpAddressManager.java b/engine/components-api/src/com/cloud/network/IpAddressManager.java
index d469cba..256f026 100644
--- a/engine/components-api/src/com/cloud/network/IpAddressManager.java
+++ b/engine/components-api/src/com/cloud/network/IpAddressManager.java
@@ -61,7 +61,7 @@ public interface IpAddressManager {
      * @return
      * @throws InsufficientAddressCapacityException
      */
-    PublicIp assignPublicIpAddress(long dcId, Long podId, Account owner, VlanType type, Long networkId, String requestedIp, boolean isSystem)
+    PublicIp assignPublicIpAddress(long dcId, Long podId, Account owner, VlanType type, Long networkId, String requestedIp, boolean isSystem, boolean forSystemVms)
             throws InsufficientAddressCapacityException;
 
     /**
diff --git a/engine/schema/resources/META-INF/db/schema-41000to41100.sql b/engine/schema/resources/META-INF/db/schema-41000to41100.sql
index 283844e..6c6655c 100644
--- a/engine/schema/resources/META-INF/db/schema-41000to41100.sql
+++ b/engine/schema/resources/META-INF/db/schema-41000to41100.sql
@@ -524,6 +524,10 @@ ADD COLUMN `forsystemvms` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'Indicates if
 ALTER TABLE `cloud`.`op_dc_ip_address_alloc`
 ADD COLUMN `vlan` INT(10) UNSIGNED NULL COMMENT 'Vlan the management network range is on';
 
+-- CLOUDSTACK-10109: Enable dedication of public IPs to SSVM and CPVM
+ALTER TABLE `cloud`.`user_ip_address`
+ADD COLUMN `forsystemvms` TINYINT(1) NOT NULL DEFAULT '0' COMMENT 'true if IP is set to system vms, false if not';
+
 -- ldap binding on domain level
 CREATE TABLE IF NOT EXISTS `cloud`.`domain_details` (
     `id` bigint unsigned NOT NULL auto_increment,
@@ -539,4 +543,3 @@ ALTER TABLE cloud.ldap_trust_map ADD COLUMN account_id BIGINT(20) DEFAULT 0;
 ALTER TABLE cloud.ldap_trust_map DROP FOREIGN KEY fk_ldap_trust_map__domain_id;
 DROP INDEX uk_ldap_trust_map__domain_id ON cloud.ldap_trust_map;
 CREATE UNIQUE INDEX uk_ldap_trust_map__bind_location ON ldap_trust_map (domain_id, account_id);
-
diff --git a/engine/schema/src/com/cloud/network/dao/IPAddressVO.java b/engine/schema/src/com/cloud/network/dao/IPAddressVO.java
index 95834a0..0b88011 100644
--- a/engine/schema/src/com/cloud/network/dao/IPAddressVO.java
+++ b/engine/schema/src/com/cloud/network/dao/IPAddressVO.java
@@ -122,6 +122,9 @@ public class IPAddressVO implements IpAddress {
     @Column(name = "rule_state")
     State ruleState;
 
+    @Column(name = "forsystemvms")
+    private boolean forSystemVms = false;
+
     @Column(name= GenericDao.REMOVED_COLUMN)
     private Date removed;
 
@@ -382,4 +385,8 @@ public class IPAddressVO implements IpAddress {
     public void setRuleState(State ruleState) {
         this.ruleState = ruleState;
     }
+
+    public boolean isForSystemVms() {
+        return forSystemVms;
+    }
 }
diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java
index e147792..68acee8 100644
--- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java
+++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java
@@ -154,7 +154,7 @@ public class BaremetaNetworkGuru extends DirectPodBasedNetworkGuru {
         DataCenter dc = _dcDao.findById(pod.getDataCenterId());
         if (nic.getIPv4Address() == null) {
             s_logger.debug(String.format("Requiring ip address: %s", nic.getIPv4Address()));
-            PublicIp ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), requiredIp, false);
+            PublicIp ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), requiredIp, false, false);
             nic.setIPv4Address(ip.getAddress().toString());
             nic.setFormat(AddressFormat.Ip4);
             nic.setIPv4Gateway(ip.getGateway());
diff --git a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/LoadBalanceRuleHandler.java b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/LoadBalanceRuleHandler.java
index f5046b6..dc5f0ab 100644
--- a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/LoadBalanceRuleHandler.java
+++ b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/LoadBalanceRuleHandler.java
@@ -446,7 +446,7 @@ public class LoadBalanceRuleHandler {
             public PublicIp doInTransaction(final TransactionStatus status) throws InsufficientAddressCapacityException {
                 final Network frontEndNetwork = _networkModel.getNetwork(guestNetworkId);
 
-                final PublicIp ip = _ipAddrMgr.assignPublicIpAddress(frontEndNetwork.getDataCenterId(), null, account, VlanType.DirectAttached, frontEndNetwork.getId(), null, true);
+                final PublicIp ip = _ipAddrMgr.assignPublicIpAddress(frontEndNetwork.getDataCenterId(), null, account, VlanType.DirectAttached, frontEndNetwork.getId(), null, true, false);
                 final IPAddressVO ipvo = _ipAddressDao.findById(ip.getId());
                 ipvo.setAssociatedWithNetworkId(frontEndNetwork.getId());
                 _ipAddressDao.update(ipvo.getId(), ipvo);
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index 49b8c15..5b1ec2e 100644
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -98,6 +98,7 @@ import com.cloud.network.as.AutoScaleVmProfileVO;
 import com.cloud.network.as.Condition;
 import com.cloud.network.as.ConditionVO;
 import com.cloud.network.as.Counter;
+import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
 import com.cloud.network.dao.LoadBalancerVO;
 import com.cloud.network.dao.NetworkVO;
@@ -163,6 +164,8 @@ import com.cloud.utils.Pair;
 import com.cloud.utils.StringUtils;
 import com.cloud.utils.db.EntityManager;
 import com.cloud.utils.net.Dhcp;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.Ip;
 import com.cloud.utils.net.NetUtils;
@@ -356,6 +359,8 @@ public class ApiResponseHelper implements ResponseGenerator {
     private ResourceTagDao _resourceTagDao;
     @Inject
     private NicExtraDhcpOptionDao _nicExtraDhcpOptionDao;
+    @Inject
+    private IPAddressDao userIpAddressDao;
 
     @Override
     public UserResponse createUserResponse(User user) {
@@ -745,6 +750,7 @@ public class ApiResponseHelper implements ResponseGenerator {
                     vlanResponse.setPhysicalNetworkId(pnw.getUuid());
                 }
             }
+            vlanResponse.setForSystemVms(isForSystemVms(vlan.getId()));
             vlanResponse.setObjectName("vlan");
             return vlanResponse;
         } catch (InstantiationException | IllegalAccessException e) {
@@ -752,6 +758,20 @@ public class ApiResponseHelper implements ResponseGenerator {
         }
     }
 
+    /**
+     * Return true if vlan IP range is dedicated for system vms (SSVM and CPVM), false if not
+     * @param vlanId vlan id
+     * @return true if VLAN IP range is dedicated to system vms
+     */
+    private boolean isForSystemVms(long vlanId){
+        SearchBuilder<IPAddressVO> sb = userIpAddressDao.createSearchBuilder();
+        sb.and("vlanId", sb.entity().getVlanId(), SearchCriteria.Op.EQ);
+        SearchCriteria<IPAddressVO> sc = sb.create();
+        sc.setParameters("vlanId", vlanId);
+        IPAddressVO userIpAddresVO = userIpAddressDao.findOneBy(sc);
+        return userIpAddresVO.isForSystemVms();
+    }
+
     @Override
     public IPAddressResponse createIPAddressResponse(ResponseView view, IpAddress ipAddr) {
         VlanVO vlan = ApiDBUtils.findVlanById(ipAddr.getVlanId());
diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
index 9d9ac52..412ca5b 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -2908,9 +2908,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         String endIPv6 = cmd.getEndIpv6();
         final String ip6Gateway = cmd.getIp6Gateway();
         final String ip6Cidr = cmd.getIp6Cidr();
+        final Boolean forSystemVms = cmd.isForSystemVms();
 
         Account vlanOwner = null;
 
+        if (forSystemVms && accountName != null) {
+            throw new InvalidParameterValueException("Account name should not be provided when ForSystemVMs is enabled");
+        }
+
         final boolean ipv4 = startIP != null;
         final boolean ipv6 = startIPv6 != null;
 
@@ -3118,12 +3123,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
             checkOverlapPrivateIpRange(zoneId, startIP, endIP);
         }
 
-        return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, forVirtualNetwork, networkId, physicalNetworkId, startIPv6, endIPv6, ip6Gateway,
+        return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, forVirtualNetwork, forSystemVms, networkId, physicalNetworkId, startIPv6, endIPv6, ip6Gateway,
                 ip6Cidr, domain, vlanOwner, network, sameSubnet);
     }
 
     private Vlan commitVlan(final Long zoneId, final Long podId, final String startIP, final String endIP, final String newVlanGatewayFinal, final String newVlanNetmaskFinal,
-            final String vlanId, final Boolean forVirtualNetwork, final Long networkId, final Long physicalNetworkId, final String startIPv6, final String endIPv6,
+            final String vlanId, final Boolean forVirtualNetwork, final Boolean forSystemVms, final Long networkId, final Long physicalNetworkId, final String startIPv6, final String endIPv6,
             final String ip6Gateway, final String ip6Cidr, final Domain domain, final Account vlanOwner, final Network network, final Pair<Boolean, Pair<String, String>> sameSubnet) {
         final GlobalLock commitVlanLock = GlobalLock.getInternLock("CommitVlan");
         commitVlanLock.lock(5);
@@ -3151,7 +3156,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                         newVlanGateway = sameSubnet.second().first();
                         newVlanNetmask = sameSubnet.second().second();
                     }
-                    final Vlan vlan = createVlanAndPublicIpRange(zoneId, networkId, physicalNetworkId, forVirtualNetwork, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId,
+                    final Vlan vlan = createVlanAndPublicIpRange(zoneId, networkId, physicalNetworkId, forVirtualNetwork, forSystemVms, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId,
                             false, domain, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr);
                     // create an entry in the nic_secondary table. This will be the new
                     // gateway that will be configured on the corresponding routervm.
@@ -3271,7 +3276,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
     @Override
     @DB
-    public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final boolean forVirtualNetwork, final Long podId, final String startIP, final String endIP,
+    public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final boolean forVirtualNetwork, final boolean forSystemVms, final Long podId, final String startIP, final String endIP,
             final String vlanGateway, final String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, final Account vlanOwner, final String startIPv6, final String endIPv6, final String vlanIp6Gateway, final String vlanIp6Cidr) {
         final Network network = _networkModel.getNetwork(networkId);
 
@@ -3521,14 +3526,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
         // Everything was fine, so persist the VLAN
         final VlanVO vlan = commitVlanAndIpRange(zoneId, networkId, physicalNetworkId, podId, startIP, endIP, vlanGateway, vlanNetmask, vlanId, domain, vlanOwner, vlanIp6Gateway, vlanIp6Cidr,
-                ipv4, zone, vlanType, ipv6Range, ipRange);
+                ipv4, zone, vlanType, ipv6Range, ipRange, forSystemVms);
 
         return vlan;
     }
 
     private VlanVO commitVlanAndIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final Long podId, final String startIP, final String endIP,
             final String vlanGateway, final String vlanNetmask, final String vlanId, final Domain domain, final Account vlanOwner, final String vlanIp6Gateway, final String vlanIp6Cidr,
-            final boolean ipv4, final DataCenterVO zone, final VlanType vlanType, final String ipv6Range, final String ipRange) {
+            final boolean ipv4, final DataCenterVO zone, final VlanType vlanType, final String ipv6Range, final String ipRange, final boolean forSystemVms) {
         return Transaction.execute(new TransactionCallback<VlanVO>() {
             @Override
             public VlanVO doInTransaction(final TransactionStatus status) {
@@ -3539,7 +3544,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                 // IPv6 use a used ip map, is different from ipv4, no need to save
                 // public ip range
                 if (ipv4) {
-                    if (!savePublicIPRange(startIP, endIP, zoneId, vlan.getId(), networkId, physicalNetworkId)) {
+                    if (!savePublicIPRange(startIP, endIP, zoneId, vlan.getId(), networkId, physicalNetworkId, forSystemVms)) {
                         throw new CloudRuntimeException("Failed to save IPv4 range. Please contact Cloud Support.");
                     }
                 }
@@ -3561,8 +3566,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                     _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size()));
                 } else if (domain != null) {
                     // This VLAN is domain-wide, so create a DomainVlanMapVO entry
-                    final DomainVlanMapVO domainVlanMapVO = new DomainVlanMapVO(domain.getId(), vlan.getId());
-                    _domainVlanMapDao.persist(domainVlanMapVO);
+                    //final DomainVlanMapVO domainVlanMapVO = new DomainVlanMapVO(domain.getId(), vlan.getId());
+                    //_domainVlanMapDao.persist(domainVlanMapVO);
                 } else if (podId != null) {
                     // This VLAN is pod-wide, so create a PodVlanMapVO entry
                     final PodVlanMapVO podVlanMapVO = new PodVlanMapVO(podId, vlan.getId());
@@ -3873,7 +3878,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
     }
 
     @DB
-    protected boolean savePublicIPRange(final String startIP, final String endIP, final long zoneId, final long vlanDbId, final long sourceNetworkid, final long physicalNetworkId) {
+    protected boolean savePublicIPRange(final String startIP, final String endIP, final long zoneId, final long vlanDbId, final long sourceNetworkid, final long physicalNetworkId, final boolean forSystemVms) {
         final long startIPLong = NetUtils.ip2Long(startIP);
         final long endIPLong = NetUtils.ip2Long(endIP);
 
@@ -3881,7 +3886,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
             @Override
             public List<String> doInTransaction(final TransactionStatus status) {
                 final IPRangeConfig config = new IPRangeConfig();
-                return config.savePublicIPRange(TransactionLegacy.currentTxn(), startIPLong, endIPLong, zoneId, vlanDbId, sourceNetworkid, physicalNetworkId);
+                return config.savePublicIPRange(TransactionLegacy.currentTxn(), startIPLong, endIPLong, zoneId, vlanDbId, sourceNetworkid, physicalNetworkId, forSystemVms);
             }
         });
 
diff --git a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java
index 1e6271d..166fab7 100644
--- a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java
+++ b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java
@@ -538,7 +538,7 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase
                             // acquire a public IP to associate with lb appliance (used as subnet IP to make the appliance part of private network)
                             PublicIp publicIp =
                                 _ipAddrMgr.assignPublicIpAddress(guestConfig.getDataCenterId(), null, _accountMgr.getSystemAccount(), VlanType.VirtualNetwork, null,
-                                    null, false);
+                                    null, false, false);
                             String publicIPNetmask = publicIp.getVlanNetmask();
                             String publicIPgateway = publicIp.getVlanGateway();
                             String publicIP = publicIp.getAddress().toString();
@@ -813,7 +813,7 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase
                         try {
                             PublicIp directIp =
                                 _ipAddrMgr.assignPublicIpAddress(network.getDataCenterId(), null, _accountDao.findById(network.getAccountId()), VlanType.DirectAttached,
-                                    network.getId(), null, true);
+                                    network.getId(), null, true, false);
                             loadBalancingIpAddress = directIp.getAddress().addr();
                         } catch (InsufficientCapacityException capException) {
                             String msg = "Ran out of guest IP addresses from the shared network.";
diff --git a/server/src/com/cloud/network/IpAddressManagerImpl.java b/server/src/com/cloud/network/IpAddressManagerImpl.java
index dca994d..891dddf 100644
--- a/server/src/com/cloud/network/IpAddressManagerImpl.java
+++ b/server/src/com/cloud/network/IpAddressManagerImpl.java
@@ -295,6 +295,10 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
 
     static Boolean rulesContinueOnErrFlag = true;
 
+    private static final ConfigKey<Boolean> SystemVmPublicIpReservationModeStrictness = new ConfigKey<Boolean>("Advanced",
+            Boolean.class, "system.vm.public.ip.reservation.mode.strictness", "false",
+            "If enabled, the use of System VMs public IP reservation is strict, preferred if not.", false, ConfigKey.Scope.Global);
+
     @Override
     public boolean configure(String name, Map<String, Object> params) {
         // populate providers
@@ -395,6 +399,9 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
         AssignIpAddressSearch.and("dc", AssignIpAddressSearch.entity().getDataCenterId(), Op.EQ);
         AssignIpAddressSearch.and("allocated", AssignIpAddressSearch.entity().getAllocatedTime(), Op.NULL);
         AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.IN);
+        if (SystemVmPublicIpReservationModeStrictness.value()) {
+            AssignIpAddressSearch.and("forSystemVms", AssignIpAddressSearch.entity().isForSystemVms(), Op.EQ);
+        }
         SearchBuilder<VlanVO> vlanSearch = _vlanDao.createSearchBuilder();
         vlanSearch.and("type", vlanSearch.entity().getVlanType(), Op.EQ);
         vlanSearch.and("networkId", vlanSearch.entity().getNetworkId(), Op.EQ);
@@ -675,20 +682,20 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
     }
 
     @Override
-    public PublicIp assignPublicIpAddress(long dcId, Long podId, Account owner, VlanType type, Long networkId, String requestedIp, boolean isSystem)
+    public PublicIp assignPublicIpAddress(long dcId, Long podId, Account owner, VlanType type, Long networkId, String requestedIp, boolean isSystem, boolean forSystemVms)
             throws InsufficientAddressCapacityException {
-        return fetchNewPublicIp(dcId, podId, null, owner, type, networkId, false, true, requestedIp, isSystem, null, null);
+        return fetchNewPublicIp(dcId, podId, null, owner, type, networkId, false, true, requestedIp, isSystem, null, null, forSystemVms);
     }
 
     @Override
     public PublicIp assignPublicIpAddressFromVlans(long dcId, Long podId, Account owner, VlanType type, List<Long> vlanDbIds, Long networkId, String requestedIp, boolean isSystem)
             throws InsufficientAddressCapacityException {
-        return fetchNewPublicIp(dcId, podId, vlanDbIds, owner, type, networkId, false, true, requestedIp, isSystem, null, null);
+        return fetchNewPublicIp(dcId, podId, vlanDbIds, owner, type, networkId, false, true, requestedIp, isSystem, null, null, false);
     }
 
     @DB
     public PublicIp fetchNewPublicIp(final long dcId, final Long podId, final List<Long> vlanDbIds, final Account owner, final VlanType vlanUse, final Long guestNetworkId,
-            final boolean sourceNat, final boolean assign, final String requestedIp, final boolean isSystem, final Long vpcId, final Boolean displayIp)
+            final boolean sourceNat, final boolean assign, final String requestedIp, final boolean isSystem, final Long vpcId, final Boolean displayIp, final boolean forSystemVms)
                     throws InsufficientAddressCapacityException {
         IPAddressVO addr = Transaction.execute(new TransactionCallbackWithException<IPAddressVO, InsufficientAddressCapacityException>() {
             @Override
@@ -758,7 +765,13 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
                     errorMessage.append(": requested ip " + requestedIp + " is not available");
                 }
 
-                Filter filter = new Filter(IPAddressVO.class, "vlanId", true, 0l, 1l);
+                boolean ascOrder = ! forSystemVms;
+                Filter filter = new Filter(IPAddressVO.class, "forSystemVms", ascOrder, 0l, 1l);
+                if (SystemVmPublicIpReservationModeStrictness.value()) {
+                    sc.setParameters("forSystemVms", forSystemVms);
+                }
+
+                filter.addOrderBy(IPAddressVO.class,"vlanId", true);
 
                 List<IPAddressVO> addrs = _ipAddressDao.search(sc, filter, false);
 
@@ -951,7 +964,13 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
                         VpcVO vpc = _vpcDao.findById(vpcId);
                         displayIp = vpc.isDisplay();
                     }
-                    return fetchNewPublicIp(dcId, null, null, owner, VlanType.VirtualNetwork, guestNtwkId, isSourceNat, true, null, false, vpcId, displayIp);
+                    PublicIp ip = fetchNewPublicIp(dcId, null, null, owner, VlanType.VirtualNetwork, guestNtwkId, isSourceNat, false, null, false, vpcId, displayIp, false);
+                    IPAddressVO publicIp = ip.ip();
+
+                    markPublicIpAsAllocated(publicIp);
+                    _ipAddressDao.update(publicIp.getId(), publicIp);
+
+                    return ip;
                 }
             });
             if (ip.getState() != State.Allocated) {
@@ -1147,7 +1166,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
             ip = Transaction.execute(new TransactionCallbackWithException<PublicIp, InsufficientAddressCapacityException>() {
                 @Override
                 public PublicIp doInTransaction(TransactionStatus status) throws InsufficientAddressCapacityException {
-                    PublicIp ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, false, assign, null, isSystem, null, displayIp);
+                    PublicIp ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, false, assign, null, isSystem, null, displayIp, false);
 
                     if (ip == null) {
                         InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Unable to find available public IP addresses", DataCenter.class, zone
@@ -2009,7 +2028,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
                         }
 
                         if (ip == null) {
-                            ip = assignPublicIpAddress(dc.getId(), null, vm.getOwner(), VlanType.DirectAttached, network.getId(), requestedIpv4, false);
+                            ip = assignPublicIpAddress(dc.getId(), null, vm.getOwner(), VlanType.DirectAttached, network.getId(), requestedIpv4, false, false);
                         }
 
                         nic.setIPv4Address(ip.getAddress().toString());
@@ -2142,7 +2161,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
 
     @Override
     public String allocatePublicIpForGuestNic(Network network, Long podId, Account owner, String requestedIp) throws InsufficientAddressCapacityException {
-        PublicIp ip = assignPublicIpAddress(network.getDataCenterId(), podId, owner, VlanType.DirectAttached, network.getId(), requestedIp, false);
+        PublicIp ip = assignPublicIpAddress(network.getDataCenterId(), podId, owner, VlanType.DirectAttached, network.getId(), requestedIp, false, false);
         if (ip == null) {
             s_logger.debug("There is no free public ip address");
             return null;
@@ -2163,6 +2182,6 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
 
     @Override
     public ConfigKey<?>[] getConfigKeys() {
-        return new ConfigKey<?>[] {UseSystemPublicIps, RulesContinueOnError};
+        return new ConfigKey<?>[] {UseSystemPublicIps, RulesContinueOnError, SystemVmPublicIpReservationModeStrictness};
     }
 }
diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java
index 6b431f4..707055f 100644
--- a/server/src/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/com/cloud/network/NetworkServiceImpl.java
@@ -1386,7 +1386,7 @@ public class NetworkServiceImpl extends ManagerBase implements  NetworkService {
 
                     if (_accountMgr.isRootAdmin(caller.getId()) && createVlan && network != null) {
                         // Create vlan ip range
-                        _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, null, startIP, endIP, gateway, netmask, vlanId,
+                        _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, false, null, startIP, endIP, gateway, netmask, vlanId,
                                 bypassVlanOverlapCheck, null, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr);
                     }
                     return network;
diff --git a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
index f9657be..a797b24 100644
--- a/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/DirectPodBasedNetworkGuru.java
@@ -194,7 +194,7 @@ public class DirectPodBasedNetworkGuru extends DirectNetworkGuru {
                         }
 
                         if (ip == null) {
-                            ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), null, false);
+                            ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), pod.getId(), vm.getOwner(), VlanType.DirectAttached, network.getId(), null, false, false);
                         }
 
                         nic.setIPv4Address(ip.getAddress().toString());
diff --git a/server/src/com/cloud/network/guru/PublicNetworkGuru.java b/server/src/com/cloud/network/guru/PublicNetworkGuru.java
index c27543d..96146f0 100644
--- a/server/src/com/cloud/network/guru/PublicNetworkGuru.java
+++ b/server/src/com/cloud/network/guru/PublicNetworkGuru.java
@@ -115,7 +115,11 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru {
     protected void getIp(NicProfile nic, DataCenter dc, VirtualMachineProfile vm, Network network) throws InsufficientVirtualNetworkCapacityException,
         InsufficientAddressCapacityException, ConcurrentOperationException {
         if (nic.getIPv4Address() == null) {
-            PublicIp ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), null, vm.getOwner(), VlanType.VirtualNetwork, null, null, false);
+            boolean forSystemVms = false;
+            if (vm.getType().equals(VirtualMachine.Type.ConsoleProxy) || vm.getType().equals(VirtualMachine.Type.SecondaryStorageVm)) {
+                forSystemVms = true;
+            }
+            PublicIp ip = _ipAddrMgr.assignPublicIpAddress(dc.getId(), null, vm.getOwner(), VlanType.VirtualNetwork, null, null, false, forSystemVms);
             nic.setIPv4Address(ip.getAddress().toString());
             nic.setIPv4Gateway(ip.getGateway());
             nic.setIPv4Netmask(ip.getNetmask());
diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java
index 854ec13..e348051 100644
--- a/server/src/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/com/cloud/server/ConfigurationServerImpl.java
@@ -290,7 +290,7 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
                             long startIPLong = NetUtils.ip2Long(startIp);
                             long endIPLong = NetUtils.ip2Long(endIp);
                             config.savePublicIPRange(TransactionLegacy.currentTxn(), startIPLong, endIPLong, vlan.getDataCenterId(), vlan.getId(), vlan.getNetworkId(),
-                                    vlan.getPhysicalNetworkId());
+                                    vlan.getPhysicalNetworkId(), false);
                         }
                     });
 
diff --git a/server/src/com/cloud/test/IPRangeConfig.java b/server/src/com/cloud/test/IPRangeConfig.java
index 29feba6..020c828 100644
--- a/server/src/com/cloud/test/IPRangeConfig.java
+++ b/server/src/com/cloud/test/IPRangeConfig.java
@@ -431,7 +431,7 @@ public class IPRangeConfig {
         List<String> problemIPs = null;
 
         if (type.equals("public")) {
-            problemIPs = savePublicIPRange(txn, startIPLong, endIPLong, zoneId, vlanDbId, sourceNetworkId, physicalNetworkId);
+            problemIPs = savePublicIPRange(txn, startIPLong, endIPLong, zoneId, vlanDbId, sourceNetworkId, physicalNetworkId, false);
         } else if (type.equals("private")) {
             problemIPs = savePrivateIPRange(txn, startIPLong, endIPLong, podId, zoneId);
         }
@@ -445,9 +445,9 @@ public class IPRangeConfig {
         return problemIPs;
     }
 
-    public Vector<String> savePublicIPRange(TransactionLegacy txn, long startIP, long endIP, long zoneId, long vlanDbId, Long sourceNetworkId, long physicalNetworkId) {
+    public Vector<String> savePublicIPRange(TransactionLegacy txn, long startIP, long endIP, long zoneId, long vlanDbId, Long sourceNetworkId, long physicalNetworkId, boolean forSystemVms) {
         String insertSql =
-            "INSERT INTO `cloud`.`user_ip_address` (public_ip_address, data_center_id, vlan_db_id, mac_address, source_network_id, physical_network_id, uuid) VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?), ?, ?, ?)";
+            "INSERT INTO `cloud`.`user_ip_address` (public_ip_address, data_center_id, vlan_db_id, mac_address, source_network_id, physical_network_id, uuid, forsystemvms) VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?), ?, ?, ?, ?)";
         String updateSql = "UPDATE `cloud`.`data_center` set mac_address = mac_address+1 where id=?";
         Vector<String> problemIPs = new Vector<String>();
 
@@ -468,6 +468,7 @@ public class IPRangeConfig {
                 insert_stmt.setLong(5, sourceNetworkId);
                 insert_stmt.setLong(6, physicalNetworkId);
                 insert_stmt.setString(7, UUID.randomUUID().toString());
+                insert_stmt.setBoolean(8, forSystemVms);
                 insert_stmt.executeUpdate();
                 update_stmt.setLong(1, zoneId);
                 update_stmt.executeUpdate();
diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
index a624986..9057241 100644
--- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
+++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
@@ -459,7 +459,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
      * @see com.cloud.configuration.ConfigurationManager#createVlanAndPublicIpRange(long, long, long, boolean, java.lang.Long, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, com.cloud.user.Account)
      */
     @Override
-    public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP,
+    public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, boolean forSystemVms, Long podId, String startIP, String endIP,
         String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6)
         throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException {
         // TODO Auto-generated method stub
diff --git a/test/integration/smoke/test_public_ip_range.py b/test/integration/smoke/test_public_ip_range.py
index e09f7b4..624a407 100644
--- a/test/integration/smoke/test_public_ip_range.py
+++ b/test/integration/smoke/test_public_ip_range.py
@@ -25,6 +25,8 @@ from marvin.lib.utils import *
 from marvin.lib.base import *
 from marvin.lib.common import *
 import datetime
+from socket import inet_aton
+from struct import unpack
 
 class TestDedicatePublicIPRange(cloudstackTestCase):
 
@@ -147,3 +149,241 @@ class TestDedicatePublicIPRange(cloudstackTestCase):
 
         return
 
+    @attr(tags = ["advanced", "publiciprange", "dedicate", "release"], required_hardware="false")
+    def test_dedicate_public_ip_range_for_system_vms(self):
+        """Test public IP range dedication for SSVM and CPVM
+        """
+
+        # Validate the following:
+        # 1. Create a Public IP range for system vms
+        # 2. Created IP range should be present and marked as forsystemvms=true, verify with listVlanIpRanges
+        # 7. Delete the Public IP range
+        
+        services = {
+            "gateway":"192.168.99.1",
+            "netmask":"255.255.255.0",
+            "startip":"192.168.99.2",
+            "endip":"192.168.99.200",
+            "forvirtualnetwork":self.services["forvirtualnetwork"],
+            "zoneid":self.services["zoneid"],
+            "vlan":self.services["vlan"]
+        }
+        public_ip_range = PublicIpRange.create(
+            self.apiclient,
+            services,
+            forsystemvms = True
+        )
+        created_ip_range_response = PublicIpRange.list(
+            self.apiclient,
+            id = public_ip_range.vlan.id
+        )
+        self.assertEqual(
+            len(created_ip_range_response),
+            1,
+            "Check listVlanIpRanges response"
+        )
+        self.assertTrue(
+            created_ip_range_response[0].forsystemvms,
+            "Check forsystemvms parameter in created vlan ip range"
+        )
+        
+        # Delete range
+        public_ip_range.delete(self.apiclient)
+        
+    def get_ip_as_number(self, ip_string):
+        """ Return numeric value for ip (passed as a string)
+        """
+        packed_ip = inet_aton(ip_string)
+        return unpack(">L", packed_ip)[0]
+    
+    def is_ip_in_range(self, start_ip, end_ip, ip_to_test):
+        """ Check whether ip_to_test belongs to IP range between start_ip and end_ip
+        """
+        start = self.get_ip_as_number(start_ip)
+        end = self.get_ip_as_number(end_ip)
+        ip = self.get_ip_as_number(ip_to_test)
+        return start <= ip and ip <= end
+    
+    def wait_for_system_vm_start(self, domain_id, srv_timeout, srv_sleep, systemvmtype):
+        """ Wait until system vm is Running
+        """
+        timeout = srv_timeout
+        while True:
+            list_systemvm_response = list_ssvms(
+                self.apiclient,
+                systemvmtype=systemvmtype,
+                domainid=domain_id
+            )
+            if isinstance(list_systemvm_response, list):
+                if list_systemvm_response[0].state == 'Running':
+                    return list_systemvm_response[0].id
+            if timeout == 0:
+                raise Exception("List System VM call failed!")
+
+            time.sleep(srv_sleep)
+            timeout = timeout - 1
+        return None
+
+    def base_system_vm(self, services, systemvmtype):
+        """
+        Base for CPVM or SSVM depending on systemvmtype parameter
+        """
+
+        # Create range for system vms
+        self.debug("Creating Public IP range for system vms")
+        public_ip_range = PublicIpRange.create(
+            self.apiclient,
+            services,
+            forsystemvms = True
+        )
+
+        # List Running System VM
+        list_systemvm_response = list_ssvms(
+            self.apiclient,
+            systemvmtype=systemvmtype,
+            state='Running',
+            domainid=public_ip_range.vlan.domainid
+        )
+        self.assertTrue(
+            isinstance(list_systemvm_response, list),
+            "Check list response returns a valid list"
+        )
+        self.assertEqual(
+            len(list_systemvm_response),
+            1,
+            "Check list response size"
+        )
+
+        # Delete System VM
+        systemvm = list_systemvm_response[0]
+        self.debug("Destroying System VM: %s" % systemvm.id)
+        cmd = destroySystemVm.destroySystemVmCmd()
+        cmd.id = systemvm.id
+        self.apiclient.destroySystemVm(cmd)
+
+        # Wait for CPVM to start
+        systemvm_id = self.wait_for_system_vm_start(
+            public_ip_range.vlan.domainid,
+            self.services["timeout"],
+            self.services["sleep"],
+            systemvmtype
+        )
+        self.assertNotEqual(
+            systemvm_id,
+            None,
+            "Check CPVM id is not none"
+        )
+        list_systemvm_response = list_ssvms(
+            self.apiclient,
+            id=systemvm_id
+        )
+        self.assertEqual(
+            isinstance(list_systemvm_response, list),
+            True,
+            "Check list response returns a valid list"
+        )
+        systemvm_response = list_systemvm_response[0]
+        self.debug("System VM state after debug: %s" % systemvm_response.state)
+        self.assertEqual(
+            systemvm_response.state,
+            'Running',
+            "Check whether System VM is running or not"
+        )
+
+        # Verify System VM got IP in the created range
+        startip = services["startip"]
+        endip = services["endip"]
+        cpvm_ip = systemvm_response.publicip
+
+        self.assertTrue(
+            self.is_ip_in_range(startip, endip, cpvm_ip),
+            "Check whether System VM Public IP is in range dedicated to system vms"
+        )
+
+        # Delete System VM and IP range, so System VM can get IP from original ranges
+        self.debug("Destroying System VM: %s" % systemvm_id)
+        cmd = destroySystemVm.destroySystemVmCmd()
+        cmd.id = systemvm_id
+        self.apiclient.destroySystemVm(cmd)
+
+        domain_id = public_ip_range.vlan.domainid
+        public_ip_range.delete(self.apiclient)
+
+        # Wait for System VM to start and check System VM public IP
+        systemvm_id = self.wait_for_system_vm_start(
+            domain_id,
+            self.services["timeout"],
+            self.services["sleep"],
+            systemvmtype
+        )
+        list_systemvm_response = list_ssvms(
+            self.apiclient,
+            id=systemvm_id
+        )
+        self.assertFalse(
+            self.is_ip_in_range(startip, endip, list_systemvm_response[0].publicip),
+            "Check System VM Public IP is not in range dedicated to system vms"
+        )
+
+        return True
+
+    def exists_public_ip_range_for_system_vms(self, zoneid):
+        """
+        Return True if there exists a public IP range dedicated for system vms in zoneid
+        """
+        existing_ip_ranges_response = PublicIpRange.list(
+            self.apiclient,
+            zoneid=zoneid
+        )
+        for r in existing_ip_ranges_response:
+            if r.forsystemvms:
+                return True
+        return False
+
+    @attr(tags = ["advanced", "publiciprange", "dedicate", "release"], required_hardware="false")
+    def test_dedicate_public_ip_range_for_system_vms_cpvm(self):
+        """Test CPVM Public IP
+        """
+        self.debug("Precondition: No public IP range dedicated for system vms in the environment")
+        if self.exists_public_ip_range_for_system_vms(self.services["zoneid"]):
+            self.skipTest("An existing IP range defined for system vms, aborting test")
+        
+        services = {
+            "gateway":"192.168.100.1",
+            "netmask":"255.255.255.0",
+            "startip":"192.168.100.2",
+            "endip":"192.168.100.200",
+            "forvirtualnetwork":self.services["forvirtualnetwork"],
+            "zoneid":self.services["zoneid"],
+            "vlan":self.services["vlan"]
+        }
+
+        self.base_system_vm(
+            services,
+            'consoleproxy'
+        )
+        return
+
+    @attr(tags = ["advanced", "publiciprange", "dedicate", "release"], required_hardware="false")
+    def test_dedicate_public_ip_range_for_system_vms_ssvm(self):
+        """Test SSVM Public IP
+        """
+        self.debug("Precondition: No public IP range dedicated for system vms in the environment")
+        if self.exists_public_ip_range_for_system_vms(self.services["zoneid"]):
+            self.skipTest("An existing IP range defined for system vms, aborting test")
+
+        services = {
+            "gateway":"192.168.200.1",
+            "netmask":"255.255.255.0",
+            "startip":"192.168.200.2",
+            "endip":"192.168.200.200",
+            "forvirtualnetwork":self.services["forvirtualnetwork"],
+            "zoneid":self.services["zoneid"],
+            "vlan":self.services["vlan"]
+        }
+
+        self.base_system_vm(
+            services,
+            'secondarystoragevm'
+        )
+        return
\ No newline at end of file
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 2c5f534..a3f1043 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -3383,7 +3383,7 @@ class PublicIpRange:
         self.__dict__.update(items)
 
     @classmethod
-    def create(cls, apiclient, services, account=None, domainid=None):
+    def create(cls, apiclient, services, account=None, domainid=None, forsystemvms=None):
         """Create VlanIpRange"""
 
         cmd = createVlanIpRange.createVlanIpRangeCmd()
@@ -3401,6 +3401,8 @@ class PublicIpRange:
             cmd.account = account
         if domainid:
             cmd.domainid = domainid
+        if forsystemvms:
+            cmd.forsystemvms = forsystemvms
 
         return PublicIpRange(apiclient.createVlanIpRange(cmd).__dict__)
 
diff --git a/ui/l10n/ar.js b/ui/l10n/ar.js
index 727660e..ef30387 100644
--- a/ui/l10n/ar.js
+++ b/ui/l10n/ar.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Services",
     "label.session.expired": "Session Expired",
     "label.set.default.NIC": "Set default NIC",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Set up zone type",
     "label.settings": "Settings",
     "label.setup": "التثبيت",
diff --git a/ui/l10n/ca.js b/ui/l10n/ca.js
index 771743f..7bd1436 100644
--- a/ui/l10n/ca.js
+++ b/ui/l10n/ca.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Services",
     "label.session.expired": "Session Expired",
     "label.set.default.NIC": "Set default NIC",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Set up zone type",
     "label.settings": "Settings",
     "label.setup": "Configuració",
diff --git a/ui/l10n/de_DE.js b/ui/l10n/de_DE.js
index 727dc2a..5bf300c 100644
--- a/ui/l10n/de_DE.js
+++ b/ui/l10n/de_DE.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Dienste",
     "label.session.expired": "Sitzung abgelaufen",
     "label.set.default.NIC": "Standard-NIC festlegen",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Zonentyp einrichten",
     "label.settings": "Einstellungen",
     "label.setup": "Konfiguration",
diff --git a/ui/l10n/en.js b/ui/l10n/en.js
index 1f31dce..9e2e4ae 100644
--- a/ui/l10n/en.js
+++ b/ui/l10n/en.js
@@ -1528,6 +1528,8 @@ var dictionary = {"ICMP.code":"ICMP Code",
 "label.services":"Services",
 "label.session.expired":"Session Expired",
 "label.set.default.NIC":"Set default NIC",
+"label.set.reservation": "Set reservation",
+"label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
 "label.set.up.zone.type":"Set up zone type",
 "label.settings":"Settings",
 "label.setup":"Setup",
diff --git a/ui/l10n/es.js b/ui/l10n/es.js
index 0d53b0e..599f9cb 100644
--- a/ui/l10n/es.js
+++ b/ui/l10n/es.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Servicios",
     "label.session.expired": "Session Caducada",
     "label.set.default.NIC": "Definir NIC por defecto",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Definir tipo de zona",
     "label.settings": "Configuración",
     "label.setup": "Configuración",
diff --git a/ui/l10n/fr_FR.js b/ui/l10n/fr_FR.js
index ac6f173..8f22fb7 100644
--- a/ui/l10n/fr_FR.js
+++ b/ui/l10n/fr_FR.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Services",
     "label.session.expired": "Session expirée",
     "label.set.default.NIC": "Définir NIC par défaut",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Configurer le type de zone",
     "label.settings": "Paramètres",
     "label.setup": "Configuration",
diff --git a/ui/l10n/hu.js b/ui/l10n/hu.js
index db4f218..b1f8324 100644
--- a/ui/l10n/hu.js
+++ b/ui/l10n/hu.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Szolgáltatások",
     "label.session.expired": "A munkamenet lejárt",
     "label.set.default.NIC": "Alapértelmezett NIC beállítása",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Zóna-típus beállítása",
     "label.settings": "Beállítások",
     "label.setup": "Beállítások",
diff --git a/ui/l10n/it_IT.js b/ui/l10n/it_IT.js
index 7a8caa5..02b7e50 100644
--- a/ui/l10n/it_IT.js
+++ b/ui/l10n/it_IT.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Services",
     "label.session.expired": "Session Expired",
     "label.set.default.NIC": "Set default NIC",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Configurazione del tipo di Zona",
     "label.settings": "Settings",
     "label.setup": "Installazione",
diff --git a/ui/l10n/ja_JP.js b/ui/l10n/ja_JP.js
index 7498c8f..da27a9f 100644
--- a/ui/l10n/ja_JP.js
+++ b/ui/l10n/ja_JP.js
@@ -1489,6 +1489,8 @@ var dictionary = {
     "label.services": "サービス",
     "label.session.expired": "セッションの有効期限が切れました",
     "label.set.default.NIC": "デフォルト NIC の設定",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "ゾーンの種類のセットアップ",
     "label.settings": "設定",
     "label.setup": "セットアップ",
diff --git a/ui/l10n/ko_KR.js b/ui/l10n/ko_KR.js
index 372a728..caa38bb 100644
--- a/ui/l10n/ko_KR.js
+++ b/ui/l10n/ko_KR.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Services",
     "label.session.expired": "세션 유효기간이 끊어짐",
     "label.set.default.NIC": "Set default NIC",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Zone 종류 설정",
     "label.settings": "Settings",
     "label.setup": "설정",
diff --git a/ui/l10n/nb_NO.js b/ui/l10n/nb_NO.js
index d0d1d3f..49a8b85 100644
--- a/ui/l10n/nb_NO.js
+++ b/ui/l10n/nb_NO.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Tjenester",
     "label.session.expired": "Sesjon utløpt",
     "label.set.default.NIC": "Sett som standard NIC",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Oppsett av sonetype",
     "label.settings": "Innstillinger",
     "label.setup": "Oppsett",
diff --git a/ui/l10n/nl_NL.js b/ui/l10n/nl_NL.js
index 62313e9..3d20ff3 100644
--- a/ui/l10n/nl_NL.js
+++ b/ui/l10n/nl_NL.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Diensten",
     "label.session.expired": "Sessie Verlopen",
     "label.set.default.NIC": "Stel standaard NIC in",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Stel zone type in",
     "label.settings": "Instellingen",
     "label.setup": "Instellen",
diff --git a/ui/l10n/pl.js b/ui/l10n/pl.js
index 75c3af1..7faa8c6 100644
--- a/ui/l10n/pl.js
+++ b/ui/l10n/pl.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Services",
     "label.session.expired": "Session Expired",
     "label.set.default.NIC": "Set default NIC",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Set up zone type",
     "label.settings": "Settings",
     "label.setup": "Konfiguracja",
diff --git a/ui/l10n/pt_BR.js b/ui/l10n/pt_BR.js
index 1b0be18..a6041f3 100644
--- a/ui/l10n/pt_BR.js
+++ b/ui/l10n/pt_BR.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Serviços",
     "label.session.expired": "Sessão Expirada",
     "label.set.default.NIC": "Configurar para NIC padrão",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Configurar tipo de zona",
     "label.settings": "Ajustes",
     "label.setup": "Configuração",
diff --git a/ui/l10n/ru_RU.js b/ui/l10n/ru_RU.js
index 62c2602..38b78d5 100644
--- a/ui/l10n/ru_RU.js
+++ b/ui/l10n/ru_RU.js
@@ -1488,6 +1488,8 @@ var dictionary = {
     "label.services": "Services",
     "label.session.expired": "Сеанс завершен",
     "label.set.default.NIC": "Установить NIC по умолчанию",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "Настроить тип зоны",
     "label.settings": "Настройки",
     "label.setup": "Настройка",
diff --git a/ui/l10n/zh_CN.js b/ui/l10n/zh_CN.js
index 78729d9..9115e94 100644
--- a/ui/l10n/zh_CN.js
+++ b/ui/l10n/zh_CN.js
@@ -1489,6 +1489,8 @@ var dictionary = {
     "label.services": "服务",
     "label.session.expired": "会话已过期",
     "label.set.default.NIC": "设置默认 NIC",
+    "label.set.reservation": "Set reservation",
+    "label.set.reservation.desc": "(optional) Please specify an account to be associated with this IP range.<br/><br/>System VMs: Enable dedication of public IP range for SSVM and CPVM, account field disabled. Reservation strictness defined on 'system.vm.public.ip.reservation.mode.strictness'",
     "label.set.up.zone.type": "设置资源域类型",
     "label.settings": "设置",
     "label.setup": "设置",
diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js
index a8ab405..d25dca5 100755
--- a/ui/scripts/docs.js
+++ b/ui/scripts/docs.js
@@ -1340,5 +1340,8 @@ cloudStack.docs = {
     },
     helpLdapLinkDomainAdmin: {
         desc: 'domain admin of the linked domain. Specify a username in GROUP/OU of LDAP'
+    },
+    helpSetReservationSystemVms: {
+        desc: 'If enabled, IP range reservation is set for SSVM & CPVM. Global setting "system.vm.public.ip.reservation.mode.strictness" is used to control whether reservation is strict or not (preferred)'
     }
 };
diff --git a/ui/scripts/system.js b/ui/scripts/system.js
index f216423..38f6074 100755
--- a/ui/scripts/system.js
+++ b/ui/scripts/system.js
@@ -54,6 +54,12 @@
                 var data = args.data ? args.data: {
                 };
                 var fields = {
+                    systemvms: {
+                        label: 'label.system.vms',
+                        isBoolean: true,
+                        docID: 'helpSetReservationSystemVms',
+                        defaultValue: data.systemvms
+                    },
                     account: {
                         label: 'label.account',
                         defaultValue: data.account
@@ -96,22 +102,40 @@
                         success: function (json) {
                             var domain = json.listdomainsresponse.domain[0];
 
+                            if (data.forSystemVms != null) {
+                                systemvms = '<li>' + _l('label.system.vms') + ': ' + data.forSystemVms + '</li>'
+                            }
                             if (data.account != null)
                                 cloudStack.dialog.notice({
-                                    message: '<ul><li>' + _l('label.account') + ': ' + data.account + '</li>' + '<li>' + _l('label.domain') + ': ' + domain.path + '</li></ul>'
+                                    message: '<ul><li>' + _l('label.account') + ': ' + data.account + '</li>' + '<li>' + _l('label.domain') + ': ' + domain.path + '</li>' + systemvms + '</ul>'
                                 });
                             else
                                 cloudStack.dialog.notice({
-                                    message: '<ul><li>' + _l('label.domain') + ': ' + domain.path + '</li></ul>'
+                                    message: '<ul><li>' + _l('label.domain') + ': ' + domain.path + '</li>' + systemvms + '</ul>'
                                 });
                         }
                     });
                 } else {
                     cloudStack.dialog.createForm({
                         form: {
-                            title: 'label.add.account',
-                            desc: '(optional) Please specify an account to be associated with this IP range.',
-                            fields: fields
+                            title: 'label.set.reservation',
+                            desc: 'label.set.reservation.desc',
+                            fields: fields,
+                            preFilter: function(args) {
+                                var $systemvms = args.$form.find('.form-item[rel=systemvms]');
+                                var $systemvmsCb = $systemvms.find('input[type=checkbox]');
+                                var $account = args.$form.find('.form-item[rel=account]');
+                                var $accountTxt = args.$form.find('input[name=account]');
+                                $systemvmsCb.change(function() {
+                                    if ($systemvmsCb.is(':checked')) {
+                                        $accountTxt.val('');
+                                        $accountTxt.attr('disabled', true);
+                                    }
+                                    else {
+                                        $accountTxt.attr('disabled', false);
+                                    }
+                                });
+                            }
                         },
                         after: function (args) {
                             var data = cloudStack.serializeForm(args.$form);
@@ -438,7 +462,7 @@
                                             'account': {
                                                 label: 'label.account',
                                                 custom: {
-                                                    buttonLabel: 'label.add.account',
+                                                    buttonLabel: 'label.set.reservation',
                                                     action: cloudStack.publicIpRangeAccount.dialog()
                                                 }
                                             },
@@ -466,6 +490,10 @@
                                                 if (args.data.account) {
                                                     if (args.data.account.account)
                                                         array1.push("&account=" + args.data.account.account);
+                                                    if (args.data.account.systemvms) {
+                                                        systvmsval = args.data.account.systemvms == "on" ? "true" : "false"
+                                                        array1.push("&forsystemvms=" + systvmsval);
+                                                    }
                                                     array1.push("&domainid=" + args.data.account.domainid);
                                                 }
 
@@ -627,7 +655,8 @@
                                                                 account: {
                                                                     _buttonLabel: item.account ? '[' + item.domain + '] ' + item.account: item.domain,
                                                                     account: item.account,
-                                                                    domainid: item.domainid
+                                                                    domainid: item.domainid,
+                                                                    forSystemVms: item.forsystemvms
                                                                 }
                                                             });
                                                         })

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