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>