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