You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by wi...@apache.org on 2026/09/05 10:35:11 UTC

(cloudstack) branch direct-routed-network updated: Direct Routed networks: DefaultL3NetworkOffering and ROUTED in the remaining UI form

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

wido pushed a commit to branch direct-routed-network
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/direct-routed-network by this push:
     new 8275a0c3c61 Direct Routed networks: DefaultL3NetworkOffering and ROUTED in the remaining UI form
8275a0c3c61 is described below

commit 8275a0c3c6172c40e73ef92e61aeea11d7f30369
Author: Wido den Hollander <wi...@widodh.nl>
AuthorDate: Sat Sep 5 10:35:04 2026 +0000

    Direct Routed networks: DefaultL3NetworkOffering and ROUTED in the remaining UI form
    
    Add a default network offering for Direct Routed (L3) guest networks,
    DefaultL3NetworkOffering: UserData and DNS via ConfigDrive, Security
    Groups enabled - the only service set validateL3NetworkOffering()
    permits. It is created in NetworkOrchestrator.configure() like the
    other default offerings, so it appears on fresh installs and on
    upgrades alike; until now the operator had to create it by hand.
    The routed id is allocated from the ROUTED physical network's range
    (no specifyVlan).
    
    UI: the ROUTED isolation method was selectable in the zone wizard and
    in the infrastructure listing's add-physical-network form, but missing
    from the form on a zone's Physical Networks tab - add it there. Give
    the VLAN, VXLAN and ROUTED options hover descriptions explaining what
    each isolation method does, so operators understand what they are
    opting into; ROUTED's spells out the Direct Routed model (no VLANs on
    the wire, no Virtual Router, no DHCP, addressing via ConfigDrive, L3
    offerings only, KVM only).
---
 .../java/com/cloud/offering/NetworkOffering.java   |  1 +
 .../engine/orchestration/NetworkOrchestrator.java  | 22 ++++++++++++++++++++++
 ui/public/locales/en.json                          |  3 +++
 ui/src/views/infra/zone/PhysicalNetworksTab.vue    | 12 ++++++++++--
 .../zone/ZoneWizardPhysicalNetworkSetupStep.vue    |  6 +++---
 5 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java
index 5000a4f8c62..b3ab6961fd7 100644
--- a/api/src/main/java/com/cloud/offering/NetworkOffering.java
+++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java
@@ -81,6 +81,7 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity,
     public final static String DefaultL2NetworkOfferingVlan = "DefaultL2NetworkOfferingVlan";
     public final static String DefaultL2NetworkOfferingConfigDrive = "DefaultL2NetworkOfferingConfigDrive";
     public final static String DefaultL2NetworkOfferingConfigDriveVlan = "DefaultL2NetworkOfferingConfigDriveVlan";
+    public final static String DefaultL3NetworkOffering = "DefaultL3NetworkOffering";
 
     /**
      * @return name for the network offering.
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 d13d9d0f60d..24f9b83ac2a 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
@@ -557,6 +557,17 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
         sgProviders.add(Provider.SecurityGroupProvider);
         defaultSharedSGEnabledNetworkOfferingProviders.put(Service.SecurityGroup, sgProviders);
 
+        // Direct Routed (L3) networks have no Virtual Router and no DHCP: Instances learn their
+        // addressing from ConfigDrive, which also carries UserData and the DNS configuration.
+        final Map<Network.Service, Set<Network.Provider>> defaultL3NetworkOfferingProviders = new HashMap<>();
+        final Set<Provider> configDriveProvider = new HashSet<>();
+        configDriveProvider.add(Provider.ConfigDrive);
+        defaultL3NetworkOfferingProviders.put(Service.UserData, configDriveProvider);
+        defaultL3NetworkOfferingProviders.put(Service.Dns, configDriveProvider);
+        final Set<Provider> l3SecurityGroupProvider = new HashSet<>();
+        l3SecurityGroupProvider.add(Provider.SecurityGroupProvider);
+        defaultL3NetworkOfferingProviders.put(Service.SecurityGroup, l3SecurityGroupProvider);
+
         tungstenProvider.add(Provider.Tungsten);
         final Map<Network.Service, Set<Network.Provider>> defaultTungstenSharedSGEnabledNetworkOfferingProviders = new HashMap<>();
         defaultTungstenSharedSGEnabledNetworkOfferingProviders.put(Service.Connectivity, tungstenProvider);
@@ -620,6 +631,17 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
                             null, true, false, false, false, false, null, null, null, true, null, null, false);
                 }
 
+                //#3b - Direct Routed (L3) network offering: ConfigDrive for UserData and DNS,
+                // Security Groups for Instance isolation. Created on upgrade and fresh install
+                // alike, since this block runs on every management server start. The routed id
+                // is allocated from the ROUTED physical network's range (no specifyVlan).
+                if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultL3NetworkOffering) == null) {
+                    offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultL3NetworkOffering,
+                            "Offering for Direct Routed (L3) networks - public IPs routed directly to Instances, configuration via ConfigDrive (UserData and DNS), Security Groups enabled, no Virtual Router and no DHCP",
+                            TrafficType.Guest, null, false, Availability.Optional, null, defaultL3NetworkOfferingProviders, true, Network.GuestType.L3, false, null, true,
+                            null, true, false, null, false, null, true, false, false, false, false, null, null, null, true, null, null, false);
+                }
+
                 if (_networkOfferingDao.findByUniqueName(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE) == null) {
                     offering = _configMgr.createNetworkOffering(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE, "Offering for Tungsten Shared Security group enabled networks",
                             TrafficType.Guest, null, true, Availability.Optional, null, defaultTungstenSharedSGEnabledNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true,
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index f8713471ce6..78cf90618d8 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -3822,6 +3822,9 @@
 "message.ip.v6.prefix.delete": "IPv6 prefix deleted",
 "message.iso.arch": "Please select an ISO architecture",
 "message.iso.desc": "Disc image containing data or bootable media for OS.",
+"message.isolationmethod.routed.description": "Direct Routed (L3): public IPv4/IPv6 subnets are routed directly to Instances over a per-network bridge on every host. No VLAN or tunnel on the wire, no Virtual Router and no DHCP - Instances receive their address, routes and DNS through ConfigDrive. Only guest networks created from an L3 network offering (such as DefaultL3NetworkOffering) can be created on this physical network. KVM only.",
+"message.isolationmethod.vlan.description": "Guest networks are isolated from each other by 802.1Q VLAN tags on the physical network.",
+"message.isolationmethod.vxlan.description": "Guest networks are isolated from each other by VXLAN tunnels (one VNI per network) carried over the physical network.",
 "message.kubernetes.cluster.add.nodes": "Please confirm that you want to add the following nodes to the cluster",
 "message.kubernetes.cluster.delete": "Please confirm that you want to destroy the Cluster.",
 "message.kubeconfig.cluster.not.available": "Kubernetes Cluster kubeconfig not available currently.",
diff --git a/ui/src/views/infra/zone/PhysicalNetworksTab.vue b/ui/src/views/infra/zone/PhysicalNetworksTab.vue
index cccb8719805..a7c9fee7c3a 100644
--- a/ui/src/views/infra/zone/PhysicalNetworksTab.vue
+++ b/ui/src/views/infra/zone/PhysicalNetworksTab.vue
@@ -109,7 +109,7 @@
             }"
             v-focus="true"
             :placeholder="apiParams.isolationmethods.description">
-            <a-select-option v-for="i in isolationMethods" :key="i" :value="i" :label="i">{{ i }}</a-select-option>
+            <a-select-option v-for="i in isolationMethods" :key="i" :value="i" :label="i" :title="isolationMethodDescription(i)">{{ i }}</a-select-option>
           </a-select>
         </a-form-item>
         <a-form-item name="vlan" ref="vlan">
@@ -200,10 +200,18 @@ export default {
   },
   computed: {
     isolationMethods () {
-      return ['VLAN', 'VXLAN', 'GRE', 'STT', 'BCF_SEGMENT', 'SSP', 'ODL', 'L3VPN', 'VCS', 'NSX', 'NETRIS']
+      return ['VLAN', 'VXLAN', 'ROUTED', 'GRE', 'STT', 'BCF_SEGMENT', 'SSP', 'ODL', 'L3VPN', 'VCS', 'NSX', 'NETRIS']
     }
   },
   methods: {
+    isolationMethodDescription (method) {
+      const keys = {
+        VLAN: 'message.isolationmethod.vlan.description',
+        VXLAN: 'message.isolationmethod.vxlan.description',
+        ROUTED: 'message.isolationmethod.routed.description'
+      }
+      return method in keys ? this.$t(keys[method]) : method
+    },
     fetchData () {
       this.fetchLoading = true
       getAPI('listPhysicalNetworks', { zoneid: this.resource.id }).then(json => {
diff --git a/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue b/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue
index 759fe1421e4..b8050ffd832 100644
--- a/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue
+++ b/ui/src/views/infra/zone/ZoneWizardPhysicalNetworkSetupStep.vue
@@ -56,9 +56,9 @@
             :filterOption="(input, option) => {
               return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }" >
-            <a-select-option value="VLAN"> VLAN </a-select-option>
-            <a-select-option value="VXLAN"> VXLAN </a-select-option>
-            <a-select-option v-if="hypervisor === 'KVM'" value="ROUTED"> ROUTED </a-select-option>
+            <a-select-option value="VLAN" :title="$t('message.isolationmethod.vlan.description')"> VLAN </a-select-option>
+            <a-select-option value="VXLAN" :title="$t('message.isolationmethod.vxlan.description')"> VXLAN </a-select-option>
+            <a-select-option v-if="hypervisor === 'KVM'" value="ROUTED" :title="$t('message.isolationmethod.routed.description')"> ROUTED </a-select-option>
             <a-select-option value="GRE"> GRE </a-select-option>
             <a-select-option value="STT"> STT </a-select-option>
             <a-select-option value="BCF_SEGMENT"> BCF_SEGMENT </a-select-option>