From 90ef67bab993c95b253cc810fb0b67fe8d6fc6e8 Mon Sep 17 00:00:00 2001 From: Nicolas Vazquez Date: Sat, 6 Jan 2018 14:44:30 -0300 Subject: [PATCH] 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. --- .../admin/vlan/CreateVlanIpRangeCmd.java | 7 + .../api/response/VlanIpRangeResponse.java | 12 + .../configuration/ConfigurationManager.java | 2 +- .../com/cloud/network/IpAddressManager.java | 2 +- .../META-INF/db/schema-41000to41100.sql | 5 +- .../com/cloud/network/dao/IPAddressVO.java | 7 + .../networkservice/BaremetaNetworkGuru.java | 2 +- .../network/lb/LoadBalanceRuleHandler.java | 2 +- .../src/com/cloud/api/ApiResponseHelper.java | 20 ++ .../ConfigurationManagerImpl.java | 27 +- ...ExternalLoadBalancerDeviceManagerImpl.java | 4 +- .../cloud/network/IpAddressManagerImpl.java | 39 ++- .../com/cloud/network/NetworkServiceImpl.java | 2 +- .../guru/DirectPodBasedNetworkGuru.java | 2 +- .../cloud/network/guru/PublicNetworkGuru.java | 6 +- .../cloud/server/ConfigurationServerImpl.java | 2 +- server/src/com/cloud/test/IPRangeConfig.java | 7 +- .../vpc/MockConfigurationManagerImpl.java | 2 +- .../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 a2da7dbc8da..fa66fdde1a7 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 59214847a92..5656a92955d 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 0c7ed3a3dc1..235b241264c 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> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent, Map 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 d469cbac62b..256f0266635 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 283844e96d6..6c6655c8e46 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 95834a0ca67..0b880116159 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 e1477926091..68acee88abe 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 f5046b6a7b4..dc5f0ab6cee 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 49b8c150ecc..5b1ec2e68b6 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 sb = userIpAddressDao.createSearchBuilder(); + sb.and("vlanId", sb.entity().getVlanId(), SearchCriteria.Op.EQ); + SearchCriteria 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 9d9ac52fae5..412ca5bead7 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> 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() { @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 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 1e6271de6a2..166fab734d4 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 dca994d6a5a..891dddf66f8 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 SystemVmPublicIpReservationModeStrictness = new ConfigKey("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 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 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 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 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() { @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 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() { @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 6b431f43a7e..707055fc593 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 f9657be4775..a797b24471a 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 c27543dbbdb..96146f0b065 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 854ec136b49..e348051cb0d 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 29feba6119e..020c828a828 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 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 savePublicIPRange(TransactionLegacy txn, long startIP, long endIP, long zoneId, long vlanDbId, Long sourceNetworkId, long physicalNetworkId) { + public Vector 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 problemIPs = new Vector(); @@ -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 a624986c446..9057241cc78 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 e09f7b43b5d..624a407dc4a 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 2c5f5345f07..a3f1043e654 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 727660eab2e..ef30387221d 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.

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 771743f3aff..7bd14365185 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.

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 727dc2afead..5bf300cffbe 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.

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 1f31dce7e73..9e2e4ae9cc9 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.

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 0d53b0e6aa6..599f9cb0420 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.

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 ac6f17323f6..8f22fb73197 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.

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 db4f2188a21..b1f83243f07 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.

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 7a8caa545de..02b7e50a81f 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.

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 7498c8f3e03..da27a9f1d43 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.

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 372a728f7c4..caa38bbeca2 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.

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 d0d1d3fbf6f..49a8b8530cf 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.

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 62313e996ad..3d20ff394c4 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.

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 75c3af1f1e8..7faa8c68e95 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.

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 1b0be1897f4..a6041f3ffb6 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.

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 62c2602f6d6..38b78d54452 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.

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 78729d9a500..9115e94cf0d 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.

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 a8ab405d754..d25dca57998 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 f216423d4bf..38f6074a3d9 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 = '
  • ' + _l('label.system.vms') + ': ' + data.forSystemVms + '
  • ' + } if (data.account != null) cloudStack.dialog.notice({ - message: '
    • ' + _l('label.account') + ': ' + data.account + '
    • ' + '
    • ' + _l('label.domain') + ': ' + domain.path + '
    ' + message: '
    • ' + _l('label.account') + ': ' + data.account + '
    • ' + '
    • ' + _l('label.domain') + ': ' + domain.path + '
    • ' + systemvms + '
    ' }); else cloudStack.dialog.notice({ - message: '
    • ' + _l('label.domain') + ': ' + domain.path + '
    ' + message: '
    • ' + _l('label.domain') + ': ' + domain.path + '
    • ' + systemvms + '
    ' }); } }); } 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 } }); })