diff --git a/api/src/main/java/com/cloud/network/Networks.java b/api/src/main/java/com/cloud/network/Networks.java index 559a369b4d9..f892e253b35 100644 --- a/api/src/main/java/com/cloud/network/Networks.java +++ b/api/src/main/java/com/cloud/network/Networks.java @@ -20,6 +20,7 @@ import java.net.URI; import java.net.URISyntaxException; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.lang3.StringUtils; /** * Network includes all of the enums used within networking. @@ -253,20 +254,42 @@ public class Networks { Long.parseLong(candidate); return Vlan.toUri(candidate); } catch (NumberFormatException nfe) { - if (com.cloud.dc.Vlan.UNTAGGED.equalsIgnoreCase(candidate)) { - return Native.toUri(candidate); - } - try { - URI uri = new URI(candidate); - BroadcastDomainType tiep = getSchemeValue(uri); - if (tiep.scheme != null && tiep.scheme.equals(uri.getScheme())) { - return uri; - } else { - throw new CloudRuntimeException("string '" + candidate + "' has an unknown BroadcastDomainType."); - } - } catch (URISyntaxException e) { - throw new CloudRuntimeException("string is not a broadcast URI: " + candidate); + return getVlanUriWhenNumberFormatException(candidate); + } + } + + /** + * This method is called in case of NumberFormatException is thrown when parsing the String into long + */ + private static URI getVlanUriWhenNumberFormatException(String candidate) { + if(StringUtils.isBlank(candidate)) { + throw new CloudRuntimeException("Expected VLAN or VXLAN but got a null isolation method"); + } + if (com.cloud.dc.Vlan.UNTAGGED.equalsIgnoreCase(candidate)) { + return Native.toUri(candidate); + } + try { + URI uri = new URI(candidate); + BroadcastDomainType tiep = getSchemeValue(uri); + if (tiep.scheme != null && tiep.scheme.equals(uri.getScheme())) { + return uri; + } else { + throw new CloudRuntimeException("string '" + candidate + "' has an unknown BroadcastDomainType."); } + } catch (URISyntaxException e) { + throw new CloudRuntimeException("string is not a broadcast URI: " + candidate); + } + } + + /** + * Encodes a string into a BroadcastUri, according to the given BroadcastDomainType + */ + public static URI encodeStringIntoBroadcastUri(String candidate, BroadcastDomainType isolationMethod) { + try{ + Long.parseLong(candidate); + return isolationMethod.toUri(candidate); + } catch (NumberFormatException nfe) { + return getVlanUriWhenNumberFormatException(candidate); } } }; diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index ab2d8f3e55c..804775a3614 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -2311,7 +2311,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } if (vlanSpecified) { - URI uri = BroadcastDomainType.fromString(vlanId); + URI uri = encodeVlanIdIntoBroadcastUri(vlanId, pNtwk); // Aux: generate secondary URI for secondary VLAN ID (if provided) for performing checks URI secondaryUri = isNotBlank(isolatedPvlan) ? BroadcastDomainType.fromString(isolatedPvlan) : null; //don't allow to specify vlan tag used by physical network for dynamic vlan allocation @@ -2465,7 +2465,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra //Logical router's UUID provided as VLAN_ID userNetwork.setVlanIdAsUUID(vlanIdFinal); //Set transient field } else { - uri = BroadcastDomainType.fromString(vlanIdFinal); + uri = encodeVlanIdIntoBroadcastUri(vlanIdFinal, pNtwk); } userNetwork.setBroadcastUri(uri); if (!vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) { @@ -2523,6 +2523,25 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return network; } + /** + * Encodes VLAN/VXLAN ID into a Broadcast URI according to the isolation method from the Physical Network. + * @return Broadcast URI, e.g. 'vlan://vlan_ID' or 'vxlan://vlxan_ID' + */ + protected URI encodeVlanIdIntoBroadcastUri(String vlanId, PhysicalNetwork pNtwk) { + if (pNtwk == null) { + throw new InvalidParameterValueException(String.format("Failed to encode VLAN/VXLAN %s into a Broadcast URI. Physical Network cannot be null.", vlanId)); + } + + if(StringUtils.isNotBlank(pNtwk.getIsolationMethods().get(0))) { + String isolationMethod = pNtwk.getIsolationMethods().get(0).toLowerCase(); + String vxlan = BroadcastDomainType.Vxlan.toString().toLowerCase(); + if(isolationMethod.equals(vxlan)) { + return BroadcastDomainType.encodeStringIntoBroadcastUri(vlanId, BroadcastDomainType.Vxlan); + } + } + return BroadcastDomainType.fromString(vlanId); + } + /** * Checks bypass VLAN id/range overlap check during network creation for guest networks * @param bypassVlanOverlapCheck bypass VLAN id/range overlap check diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java index 3450c09b263..ed5265ccc5a 100644 --- a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java @@ -22,12 +22,15 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; @@ -463,4 +466,75 @@ public class NetworkOrchestratorTest extends TestCase { testOrchastrator.validateLockedRequestedIp(ipVoSpy, lockedIp); } + @Test + public void encodeVlanIdIntoBroadcastUriTestVxlan() { + encodeVlanIdIntoBroadcastUriPrepareAndTest("123", "VXLAN", "vxlan", "vxlan://123"); + } + + @Test + public void encodeVlanIdIntoBroadcastUriTestVlan() { + encodeVlanIdIntoBroadcastUriPrepareAndTest("123", "VLAN", "vlan", "vlan://123"); + } + + @Test + public void encodeVlanIdIntoBroadcastUriTestEmpty() { + encodeVlanIdIntoBroadcastUriPrepareAndTest("123", "", "vlan", "vlan://123"); + } + + @Test + public void encodeVlanIdIntoBroadcastUriTestNull() { + encodeVlanIdIntoBroadcastUriPrepareAndTest("123", null, "vlan", "vlan://123"); + } + + @Test(expected = CloudRuntimeException.class) + public void encodeVlanIdIntoBroadcastUriTestEmptyVlanId() { + encodeVlanIdIntoBroadcastUriPrepareAndTest("", "vxlan", "vlan", "vlan://123"); + } + + @Test(expected = CloudRuntimeException.class) + public void encodeVlanIdIntoBroadcastUriTestNullVlanId() { + encodeVlanIdIntoBroadcastUriPrepareAndTest(null, "vlan", "vlan", "vlan://123"); + } + + @Test(expected = CloudRuntimeException.class) + public void encodeVlanIdIntoBroadcastUriTestBlankVlanId() { + encodeVlanIdIntoBroadcastUriPrepareAndTest(" ", "vlan", "vlan", "vlan://123"); + } + + @Test + public void encodeVlanIdIntoBroadcastUriTestNullVlanIdWithSchema() { + encodeVlanIdIntoBroadcastUriPrepareAndTest("vlan://123", "vlan", "vlan", "vlan://123"); + } + + @Test + public void encodeVlanIdIntoBroadcastUriTestNullVlanIdWithSchemaIsolationVxlan() { + encodeVlanIdIntoBroadcastUriPrepareAndTest("vlan://123", "vxlan", "vlan", "vlan://123"); + } + + @Test + public void encodeVlanIdIntoBroadcastUriTestNullVxlanIdWithSchema() { + encodeVlanIdIntoBroadcastUriPrepareAndTest("vxlan://123", "vxlan", "vxlan", "vxlan://123"); + } + + @Test + public void encodeVlanIdIntoBroadcastUriTestNullVxlanIdWithSchemaIsolationVlan() { + encodeVlanIdIntoBroadcastUriPrepareAndTest("vxlan://123", "vlan", "vxlan", "vxlan://123"); + } + + @Test(expected = InvalidParameterValueException.class) + public void encodeVlanIdIntoBroadcastUriTestNullNetwork() { + URI resultUri = testOrchastrator.encodeVlanIdIntoBroadcastUri("vxlan://123", null); + } + + private void encodeVlanIdIntoBroadcastUriPrepareAndTest(String vlanId, String isolationMethod, String expectedIsolation, String expectedUri) { + PhysicalNetworkVO physicalNetwork = new PhysicalNetworkVO(); + List isolationMethods = new ArrayList<>(); + isolationMethods.add(isolationMethod); + physicalNetwork.setIsolationMethods(isolationMethods); + + URI resultUri = testOrchastrator.encodeVlanIdIntoBroadcastUri(vlanId, physicalNetwork); + + Assert.assertEquals(expectedIsolation, resultUri.getScheme()); + Assert.assertEquals(expectedUri, resultUri.toString()); + } }