Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
e608669
Network Extension: Orchestrate external Network devices
weizhouapache Apr 15, 2026
6ff915f
gha: fix EOF and license
weizhouapache Apr 15, 2026
eb4e8bc
add unit tests
weizhouapache Apr 15, 2026
f44cd7e
gha: fix EOF again
weizhouapache Apr 15, 2026
ab2306b
api/server: revert changes on updatePhysicalNetwork
weizhouapache Apr 16, 2026
37b7a2f
ExtensionsManagerImpl: minor changes
weizhouapache Apr 16, 2026
f8201c6
UI: list extensions with type=Orchestrator when register template/iso…
weizhouapache Apr 16, 2026
7af452d
api/server: apply suggestions
weizhouapache Apr 16, 2026
91af39b
network extension: add service CustomAction
weizhouapache Apr 16, 2026
8454a7f
NE: UI cleanup
weizhouapache Apr 16, 2026
f7b5723
NE: more unit tests and UI optimization
weizhouapache Apr 17, 2026
bfbcf51
test: log custom actions
weizhouapache Apr 17, 2026
e9b6c2f
NE: apply copilot's suggestions
weizhouapache Apr 17, 2026
8c42632
NE: check vpc CustomAction provider instead of first tier and cleanup UI
weizhouapache Apr 29, 2026
5dd7afd
Update framework/extensions/src/main/java/org/apache/cloudstack/frame…
weizhouapache Apr 30, 2026
0ccc8e2
NE: VIF binding hooks for OVS-backed extensions
msinhore Apr 30, 2026
8d07652
NE: persist OVN broadcast/isolation URI on NIC for vif.binding=lswitch
msinhore May 3, 2026
70459a3
NE: persist OVN broadcast type and URI on Network for vif.binding=lsw…
msinhore May 4, 2026
99540cc
UI: fix provider list when add vpc offering
weizhouapache Apr 30, 2026
fe9abb6
extension: remove resourceId and resourceType from listExtensions
weizhouapache Apr 30, 2026
a9a3c56
server: optimize listSupportedNetworkServiceProviders
weizhouapache Apr 30, 2026
607daba
framework: Add command constants
weizhouapache Apr 30, 2026
550b4ca
test: add services (SourceNat,StaticNat,PortForwarding,Firewall,Lb,Us…
weizhouapache May 6, 2026
275b727
create method runVirtualMachineCustomAction
weizhouapache May 7, 2026
9b2a8f2
NE: refactor the ovn support
weizhouapache May 11, 2026
0b68dbd
NE: pass physical network and network details and payload in a JSON file
weizhouapache May 13, 2026
0e991b9
NE: update broadcast_uri of existing nics
weizhouapache May 25, 2026
e97e6e8
NE: apply Vishesh's suggestion
weizhouapache May 26, 2026
f9a9578
NE: pass network/nic IPv6 information to extension
weizhouapache May 29, 2026
54c02c3
NE: apply copilot's suggestions
weizhouapache Jun 1, 2026
bc395f6
Apply Vishesh's suggestions
weizhouapache Jun 1, 2026
42b960a
NE: apply Vishesh's suggestion part2
weizhouapache Jun 2, 2026
bfac8af
NE: apply Vishesh's suggestion part3
weizhouapache Jun 3, 2026
1b8716b
NE: add IPv6 address to logging and restore data
weizhouapache Jun 3, 2026
64275da
NE; remove set-dhcp-options
weizhouapache Jun 3, 2026
bf03b08
NE: add commands for prepare nic and release nic
weizhouapache Jun 3, 2026
68dab38
NE: replace json string with json object
weizhouapache Jun 3, 2026
e9b05f5
NE: add prepare-nic / release-nic in README.md
weizhouapache Jun 4, 2026
a9419f2
NE: consider vpc tiers in resolveExtensionForVpc
weizhouapache Jun 4, 2026
93e62e6
NE: add method addNicToPayload
weizhouapache Jun 4, 2026
d8943c7
NE: Add addExtensionIpToPayload and addNetworkDnsToPayload
weizhouapache Jun 4, 2026
cb3abc1
NE: add addPublicIpToPayload
weizhouapache Jun 4, 2026
efbe58e
NE: change cidr to vpc_cidr when implement a VPC
weizhouapache Jun 4, 2026
8dc9e4e
NE: add network guest_type to payload
weizhouapache Jun 4, 2026
c4fadec
api,engine: add method removeDnsEntry
weizhouapache Jun 9, 2026
500d588
NE: add remove-dns-entry
weizhouapache Jun 9, 2026
2390cd0
NE: add network dns and domain to dns payload
weizhouapache Jun 9, 2026
8dcb34b
NE: add nic_uuid to restore data
weizhouapache Jun 9, 2026
4f2b2f9
NE: add network dns and domain to restore data
weizhouapache Jun 9, 2026
c05d46b
NE: support NetworkExtension as an isolation method
weizhouapache Jun 9, 2026
f469e8b
NE: fix getNetworkCapabilitiesForProvider
weizhouapache Jun 10, 2026
e0873af
NE: 'network.broadcast_uri' and 'network.broadcast_domain_type' are r…
weizhouapache Jun 10, 2026
b52c124
NE: do not set createVlan to false
weizhouapache Jun 12, 2026
8dc2779
NE: support Shared networks in NetworkExtensionGuestNetworkGuru
weizhouapache Jun 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/src/main/java/com/cloud/event/EventTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,7 @@ public class EventTypes {
public static final String EVENT_EXTENSION_DELETE = "EXTENSION.DELETE";
public static final String EVENT_EXTENSION_RESOURCE_REGISTER = "EXTENSION.RESOURCE.REGISTER";
public static final String EVENT_EXTENSION_RESOURCE_UNREGISTER = "EXTENSION.RESOURCE.UNREGISTER";
public static final String EVENT_EXTENSION_RESOURCE_UPDATE = "EXTENSION.RESOURCE.UPDATE";
public static final String EVENT_EXTENSION_CUSTOM_ACTION_ADD = "EXTENSION.CUSTOM.ACTION.ADD";
public static final String EVENT_EXTENSION_CUSTOM_ACTION_UPDATE = "EXTENSION.CUSTOM.ACTION.UPDATE";
public static final String EVENT_EXTENSION_CUSTOM_ACTION_DELETE = "EXTENSION.CUSTOM.ACTION.DELETE";
Expand Down
38 changes: 38 additions & 0 deletions api/src/main/java/com/cloud/network/Network.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class Service {
public static final Service NetworkACL = new Service("NetworkACL", Capability.SupportedProtocols);
public static final Service Connectivity = new Service("Connectivity", Capability.DistributedRouter, Capability.RegionLevelVpc, Capability.StretchedL2Subnet,
Capability.NoVlan, Capability.PublicAccess);
public static final Service CustomAction = new Service("CustomAction");

private final String name;
private final Capability[] caps;
Expand Down Expand Up @@ -207,6 +208,7 @@ public static class Provider {

public static final Provider Nsx = new Provider("Nsx", false);
public static final Provider Netris = new Provider("Netris", false);
public static final Provider NetworkExtension = new Provider("NetworkExtension", false, true);

private final String name;
private final boolean isExternal;
Expand Down Expand Up @@ -250,11 +252,47 @@ public static Provider getProvider(String providerName) {
return null;
}

/** Private constructor for transient (non-registered) providers. */
private Provider(String name) {
this.name = name;
this.isExternal = false;
this.needCleanupOnShutdown = true;
// intentionally NOT added to supportedProviders
}

/**
* Creates a transient (non-registered) {@link Provider} with the given name.
*
* <p>The new instance is <em>not</em> added to {@code supportedProviders}, so it
* will never be returned by {@link #getProvider(String)} and will not pollute the
* global provider registry. Use this for dynamic / extension-backed providers
* whose names are only known at runtime (e.g. NetworkOrchestrator extensions).</p>
*
* @param name the provider name (typically the extension name)
* @return a transient {@link Provider} instance with the given name
*/
public static Provider createTransientProvider(String name) {
return new Provider(name);
}

@Override public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("name", name)
.toString();
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Provider)) return false;
Provider provider = (Provider) obj;
return this.name.equals(provider.name);
}

@Override
public int hashCode() {
return name.hashCode();
Comment thread
weizhouapache marked this conversation as resolved.
}
}

public static class Capability {
Expand Down
14 changes: 14 additions & 0 deletions api/src/main/java/com/cloud/network/NetworkModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ public interface NetworkModel {

boolean canElementEnableIndividualServices(Provider provider);

boolean canElementEnableIndividualServicesByName(String providerName);

boolean areServicesSupportedInNetwork(long networkId, Service... services);

boolean isNetworkSystem(Network network);
Expand Down Expand Up @@ -237,6 +239,18 @@ public interface NetworkModel {

String getDefaultGuestTrafficLabel(long dcId, HypervisorType vmware);

/**
* Resolves a provider name to a {@link Provider} instance.
* For known static providers, delegates to {@link Provider#getProvider(String)}.
* For dynamically-registered NetworkOrchestrator extension providers whose names
* are not in the static registry, returns a transient {@link Provider} with the
* given name so callers can still dispatch correctly.
*
* @param providerName the provider name from {@code ntwk_service_map} or similar
* @return a {@link Provider} instance, or {@code null} if not resolvable
*/
Provider resolveProvider(String providerName);

/**
* @param providerName
* @return
Expand Down
9 changes: 7 additions & 2 deletions api/src/main/java/com/cloud/network/Networks.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ public String getValueFrom(URI uri) {
return uri == null ? null : uri.getAuthority();
}
},
Vswitch("vs", String.class), LinkLocal(null, null), Vnet("vnet", Long.class), Storage("storage", Integer.class), Lswitch("lswitch", String.class) {
Vswitch("vs", String.class),
LinkLocal(null, null),
Vnet("vnet", Long.class),
Storage("storage", Integer.class),
Lswitch("lswitch", String.class) {
@Override
public <T> URI toUri(T value) {
try {
Expand All @@ -99,7 +103,8 @@ public String getValueFrom(URI uri) {
return uri == null ? null : uri.getSchemeSpecificPart();
}
},
Mido("mido", String.class), Pvlan("pvlan", String.class),
Mido("mido", String.class),
Pvlan("pvlan", String.class),
Vxlan("vxlan", Long.class) {
@Override
public <T> URI toUri(T value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ boolean configDnsSupportForSubnet(Network network, NicProfile nic, VirtualMachin
throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException;

boolean removeDnsSupportForSubnet(Network network) throws ResourceUnavailableException;

default boolean removeDnsEntry(Network network, NicProfile nic, VirtualMachineProfile vmProfile) throws ResourceUnavailableException { return true; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,8 @@ boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, Reser
* @return true/false
*/
boolean verifyServicesCombination(Set<Service> services);

default boolean rollingRestartSupported() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,12 @@ public Boolean getSuccess() {
public void setResult(Map<String, String> result) {
this.result = result;
}

public Map<String, String> getResult() {
return result;
}

public boolean isSuccess() {
return Boolean.TRUE.equals(success);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@

public interface Extension extends InternalIdentity, Identity {
enum Type {
Orchestrator
Orchestrator,
NetworkOrchestrator
}
enum State {
Enabled, Disabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@

public interface ExtensionCustomAction extends InternalIdentity, Identity {
enum ResourceType {
VirtualMachine(com.cloud.vm.VirtualMachine.class);
VirtualMachine(com.cloud.vm.VirtualMachine.class),
Network(com.cloud.network.Network.class),
Vpc(com.cloud.network.vpc.Vpc.class);

private final Class<?> clazz;

Expand Down
114 changes: 114 additions & 0 deletions api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,124 @@
package org.apache.cloudstack.extension;

import java.util.List;
import java.util.Map;

import com.cloud.network.Network.Capability;
import com.cloud.network.Network.Service;

public interface ExtensionHelper {
Long getExtensionIdForCluster(long clusterId);
Extension getExtension(long id);
Extension getExtensionForCluster(long clusterId);
List<String> getExtensionReservedResourceDetails(long extensionId);

/**
* Network Guru of Network Extension
*/
String NETWORK_EXTENSION_GURU_NAME = "NetworkExtensionGuestNetworkGuru";

/**
* Extension resource-map detail key holding the isolation method for the physical
* network (e.g. {@code "NetworkExtension"}).
* If not specified, the isolation method of physical network will be used.
*/
String NETWORK_ISOLATION_METHOD_DETAIL_KEY = "network.isolation.method";

/**
* Value of {@link #NETWORK_ISOLATION_METHOD_DETAIL_KEY} that indicates
* the extension owns the network isolation.
*/
String NETWORK_EXTENSION_ISOLATION_METHOD = "NetworkExtension";

/**
* Detail key used to store the comma-separated list of network services provided
* by a NetworkOrchestrator extension (e.g. {@code "SourceNat,StaticNat,Firewall"}).
*/
String NETWORK_SERVICES_DETAIL_KEY = "network.services";

/**
* Detail key used to store a JSON object mapping each service name to its
* CloudStack {@link com.cloud.network.Network.Capability} key/value pairs.
* Example: {@code {"SourceNat":{"SupportedSourceNatTypes":"peraccount"}}}.
* Used together with {@link #NETWORK_SERVICES_DETAIL_KEY}.
*/
String NETWORK_SERVICE_CAPABILITIES_DETAIL_KEY = "network.service.capabilities";

String getExtensionScriptPath(Extension extension);

/**
* Finds the extension registered with the given physical network whose name
* matches the given provider name (case-insensitive). Returns {@code null}
* if no matching extension is found.
*
* <p>This is the preferred lookup when multiple extensions are registered on
* the same physical network: the provider name stored in
* {@code ntwk_service_map} is used to pinpoint the exact extension that
* handles a given network.</p>
*
* @param physicalNetworkId the physical network ID
* @param providerName the provider name (must equal the extension name)
* @return the matching {@link Extension}, or {@code null}
*/
Extension getExtensionForPhysicalNetworkAndProvider(long physicalNetworkId, String providerName);

/**
* Returns ALL {@code extension_resource_map_details} (including hidden) for
* the specific extension registered on the given physical network. Used by
* {@code NetworkExtensionElement} to inject device credentials into the script
* environment for the correct extension when multiple different extensions are
* registered on the same physical network.
*
* @param physicalNetworkId the physical network ID
* @param extensionId the extension ID
* @return all key/value details including non-display ones, or an empty map
*/
Map<String, String> getAllResourceMapDetailsForExtensionOnPhysicalNetwork(long physicalNetworkId, long extensionId);

/**
* Returns {@code true} if the given provider name is backed by a
* {@code NetworkOrchestrator} extension registered on any physical network.
* This is used by {@code NetworkModelImpl} to detect extension-backed providers
* that are not in the static {@code s_providerToNetworkElementMap}.
*
* @param providerName the provider / extension name
* @return true if the provider is a NetworkExtension provider
*/
boolean isNetworkExtensionProvider(String providerName);

/**
* List all registered extensions filtered by extension {@link Extension.Type}.
* Useful for callers that need to discover available providers of a given
* type (e.g. Orchestrator, NetworkOrchestrator).
*
* @param type extension type to filter by
* @return list of matching {@link Extension} instances (empty list if none)
*/
List<Extension> listExtensionsByType(Extension.Type type);

/**
* Returns {@code true} when the extension registered for {@code providerName} on
* the given physical network has its {@link #NETWORK_ISOLATION_METHOD_DETAIL_KEY}
* resource-map detail set to {@link #NETWORK_EXTENSION_ISOLATION_METHOD}.
*
* @param providerName provider / extension name
* @return true if the extension uses NetworkExtension isolation
*/
boolean usesNetworkExtensionIsolation(String providerName);

/**
* Returns the effective {@link Service} → ({@link Capability} → value) capabilities
* for the given external network provider, looking it up by name on the given
* physical network.
*
* <p>If {@code physicalNetworkId} is {@code null}, the method searches across all
* physical networks that have extensions registered and returns the capabilities for
* the first matching extension.</p>
*
* @param physicalNetworkId physical network ID, or {@code null} for offering-level queries
* @param providerName provider / extension name
* @return capabilities map, or the default capabilities if no matching extension is found
*/
Map<Service, Map<Capability, String>> getNetworkCapabilitiesForProvider(Long physicalNetworkId, String providerName);

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@

public interface ExtensionResourceMap extends InternalIdentity, Identity {
enum ResourceType {
Cluster
Cluster,
PhysicalNetwork
}

long getExtensionId();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.apache.cloudstack.extension;

import java.util.Map;

import com.cloud.network.Network;
import com.cloud.network.vpc.Vpc;

/**
* Implemented by network elements that support running custom actions on a
* managed network or VPC (e.g. NetworkExtensionElement).
*
* <p>This interface is looked up by {@code ExtensionsManagerImpl} to dispatch
* {@code runCustomAction} requests whose resource type is {@code Network}
* or {@code Vpc}.</p>
*/
public interface NetworkCustomActionProvider {

/**
* Returns {@code true} if this provider handles networks whose physical
* network has an ExternalNetwork service provider registered.
*
* @param network the target network
* @return {@code true} if this provider can handle the network
*/
boolean canHandleCustomAction(Network network);

/**
* Returns {@code true} if this provider can handle custom actions for
* the given VPC.
*
* @param vpc the target VPC
* @return {@code true} if this provider can handle the VPC
*/
boolean canHandleVpcCustomAction(Vpc vpc);

/**
* Runs a named custom action against the external network device that
* manages the given network.
*
* @param network the CloudStack network on which to run the action
* @param actionName the action name (e.g. {@code "reboot-device"}, {@code "dump-config"})
* @param parameters optional parameters supplied by the caller
* @return output from the action script, or {@code null} on failure
*/
String runCustomAction(Network network, String actionName, Map<String, Object> parameters);

/**
* Runs a named custom action against the external network device that
* manages the given VPC.
*
* @param vpc the CloudStack VPC on which to run the action
* @param actionName the action name
* @param parameters optional parameters supplied by the caller
* @return output from the action script, or {@code null} on failure
*/
String runCustomAction(Vpc vpc, String actionName, Map<String, Object> parameters);
}
Loading
Loading