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 <[email protected]>
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>

Reply via email to