Skip to content

Commit 8996286

Browse files
Network Extension: Orchestrate external Network devices (#13032)
This PR introduces and wires a new extension model for external network orchestration in CloudStack, centered on a new extension type: NetworkOrchestrator. It extends the extension lifecycle from cluster-only registration to physical-network registration, adds API support for updating registered extension metadata, and enables automatic offering creation (network and VPC) based on provider-declared supported services and capabilities. It also adds smoke coverage (KVM-only) using a Linux network namespace based implementation. Doc PR: apache/cloudstack-documentation#649 What's new 1) New extension type: NetworkOrchestrator Adds support for creating extensions of type NetworkOrchestrator. Intended to back CloudStack network/VPC operations via an external orchestrator/provider. 2) Register extension with PhysicalNetwork (in addition to Cluster) Extensions can now be registered against a PhysicalNetwork resource. This enables network service provider behavior at physical-network scope, not only cluster scope. 3) Physical network registration details support Registered extension details for PhysicalNetwork are handled similarly to cluster registration details. Supports storing/updating external access metadata (credentials/endpoints/config details). 4) New API: update registered extension Adds API support to update extension registration metadata after registration. Useful for rotating credentials, updating endpoints, and changing external connection properties without re-registering. 5) Offering automation from external provider capabilities Network/VPC offerings can be created with the external network provider using: provider supportedservices per-service service capabilities This allows CloudStack offerings to align with what the external provider actually supports. 6) Network support via generated offerings Using offerings backed by the external provider, networks can be created and operated with supported services/capabilities. Supported operations include (based on provider capabilities): Source NAT Static NAT Port Forwarding Firewall Load Balancing DHCP DNS UserData 7) VPC support via generated offerings Using VPC offerings backed by the external provider, VPCs and tiers can be created and operated with supported services/capabilities. Supported operations include (based on provider capabilities): VPC tier creation/implementation Source NAT in VPC context Static NAT / Port Forwarding / LB on VPC tiers Network ACL association and ACL rule apply paths Related lifecycle/restart/reapply operations 8) Linux network namespace based external implementation Adds/uses a network extension implementation based on Linux network namespaces. Reference implementation: https://github.com/apache/cloudstack-extensions/tree/network-namespace/Network-Namespace 9) Smoke test coverage (KVM-only) Adds smoke tests using the namespace-based extension implementation. Scope includes provider lifecycle, offering creation, network/VPC flows, and key network services. Applicable hypervisor for this smoke suite: KVM.
1 parent 20e9980 commit 8996286

89 files changed

Lines changed: 12178 additions & 410 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/src/main/java/com/cloud/event/EventTypes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,7 @@ public class EventTypes {
875875
public static final String EVENT_EXTENSION_DELETE = "EXTENSION.DELETE";
876876
public static final String EVENT_EXTENSION_RESOURCE_REGISTER = "EXTENSION.RESOURCE.REGISTER";
877877
public static final String EVENT_EXTENSION_RESOURCE_UNREGISTER = "EXTENSION.RESOURCE.UNREGISTER";
878+
public static final String EVENT_EXTENSION_RESOURCE_UPDATE = "EXTENSION.RESOURCE.UPDATE";
878879
public static final String EVENT_EXTENSION_CUSTOM_ACTION_ADD = "EXTENSION.CUSTOM.ACTION.ADD";
879880
public static final String EVENT_EXTENSION_CUSTOM_ACTION_UPDATE = "EXTENSION.CUSTOM.ACTION.UPDATE";
880881
public static final String EVENT_EXTENSION_CUSTOM_ACTION_DELETE = "EXTENSION.CUSTOM.ACTION.DELETE";

api/src/main/java/com/cloud/network/Network.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class Service {
116116
public static final Service NetworkACL = new Service("NetworkACL", Capability.SupportedProtocols);
117117
public static final Service Connectivity = new Service("Connectivity", Capability.DistributedRouter, Capability.RegionLevelVpc, Capability.StretchedL2Subnet,
118118
Capability.NoVlan, Capability.PublicAccess);
119+
public static final Service CustomAction = new Service("CustomAction");
119120

120121
private final String name;
121122
private final Capability[] caps;
@@ -207,6 +208,7 @@ public static class Provider {
207208

208209
public static final Provider Nsx = new Provider("Nsx", false);
209210
public static final Provider Netris = new Provider("Netris", false);
211+
public static final Provider NetworkExtension = new Provider("NetworkExtension", false, true);
210212

211213
private final String name;
212214
private final boolean isExternal;
@@ -250,11 +252,47 @@ public static Provider getProvider(String providerName) {
250252
return null;
251253
}
252254

255+
/** Private constructor for transient (non-registered) providers. */
256+
private Provider(String name) {
257+
this.name = name;
258+
this.isExternal = false;
259+
this.needCleanupOnShutdown = true;
260+
// intentionally NOT added to supportedProviders
261+
}
262+
263+
/**
264+
* Creates a transient (non-registered) {@link Provider} with the given name.
265+
*
266+
* <p>The new instance is <em>not</em> added to {@code supportedProviders}, so it
267+
* will never be returned by {@link #getProvider(String)} and will not pollute the
268+
* global provider registry. Use this for dynamic / extension-backed providers
269+
* whose names are only known at runtime (e.g. NetworkOrchestrator extensions).</p>
270+
*
271+
* @param name the provider name (typically the extension name)
272+
* @return a transient {@link Provider} instance with the given name
273+
*/
274+
public static Provider createTransientProvider(String name) {
275+
return new Provider(name);
276+
}
277+
253278
@Override public String toString() {
254279
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
255280
.append("name", name)
256281
.toString();
257282
}
283+
284+
@Override
285+
public boolean equals(Object obj) {
286+
if (this == obj) return true;
287+
if (!(obj instanceof Provider)) return false;
288+
Provider provider = (Provider) obj;
289+
return this.name.equals(provider.name);
290+
}
291+
292+
@Override
293+
public int hashCode() {
294+
return name.hashCode();
295+
}
258296
}
259297

260298
public static class Capability {

api/src/main/java/com/cloud/network/NetworkModel.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ public interface NetworkModel {
187187

188188
boolean canElementEnableIndividualServices(Provider provider);
189189

190+
boolean canElementEnableIndividualServicesByName(String providerName);
191+
190192
boolean areServicesSupportedInNetwork(long networkId, Service... services);
191193

192194
boolean isNetworkSystem(Network network);
@@ -237,6 +239,18 @@ public interface NetworkModel {
237239

238240
String getDefaultGuestTrafficLabel(long dcId, HypervisorType vmware);
239241

242+
/**
243+
* Resolves a provider name to a {@link Provider} instance.
244+
* For known static providers, delegates to {@link Provider#getProvider(String)}.
245+
* For dynamically-registered NetworkOrchestrator extension providers whose names
246+
* are not in the static registry, returns a transient {@link Provider} with the
247+
* given name so callers can still dispatch correctly.
248+
*
249+
* @param providerName the provider name from {@code ntwk_service_map} or similar
250+
* @return a {@link Provider} instance, or {@code null} if not resolvable
251+
*/
252+
Provider resolveProvider(String providerName);
253+
240254
/**
241255
* @param providerName
242256
* @return

api/src/main/java/com/cloud/network/Networks.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ public String getValueFrom(URI uri) {
8181
return uri == null ? null : uri.getAuthority();
8282
}
8383
},
84-
Vswitch("vs", String.class), LinkLocal(null, null), Vnet("vnet", Long.class), Storage("storage", Integer.class), Lswitch("lswitch", String.class) {
84+
Vswitch("vs", String.class),
85+
LinkLocal(null, null),
86+
Vnet("vnet", Long.class),
87+
Storage("storage", Integer.class),
88+
Lswitch("lswitch", String.class) {
8589
@Override
8690
public <T> URI toUri(T value) {
8791
try {
@@ -99,7 +103,8 @@ public String getValueFrom(URI uri) {
99103
return uri == null ? null : uri.getSchemeSpecificPart();
100104
}
101105
},
102-
Mido("mido", String.class), Pvlan("pvlan", String.class),
106+
Mido("mido", String.class),
107+
Pvlan("pvlan", String.class),
103108
Vxlan("vxlan", Long.class) {
104109
@Override
105110
public <T> URI toUri(T value) {

api/src/main/java/com/cloud/network/element/DnsServiceProvider.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ boolean configDnsSupportForSubnet(Network network, NicProfile nic, VirtualMachin
3333
throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException;
3434

3535
boolean removeDnsSupportForSubnet(Network network) throws ResourceUnavailableException;
36+
37+
default boolean removeDnsEntry(Network network, NicProfile nic, VirtualMachineProfile vmProfile) throws ResourceUnavailableException { return true; }
3638
}

api/src/main/java/com/cloud/network/element/NetworkElement.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,8 @@ boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, Reser
146146
* @return true/false
147147
*/
148148
boolean verifyServicesCombination(Set<Service> services);
149+
150+
default boolean rollingRestartSupported() {
151+
return true;
152+
}
149153
}

api/src/main/java/com/cloud/vm/NicProfile.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,6 @@ public String toString() {
463463
return String.format("NicProfile %s",
464464
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
465465
this, "id", "uuid", "vmId", "deviceId",
466-
"broadcastUri", "reservationId", "iPv4Address"));
466+
"broadcastType", "broadcastUri", "reservationId", "iPv4Address"));
467467
}
468468
}

api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,12 @@ public Boolean getSuccess() {
6262
public void setResult(Map<String, String> result) {
6363
this.result = result;
6464
}
65+
66+
public Map<String, String> getResult() {
67+
return result;
68+
}
69+
70+
public boolean isSuccess() {
71+
return Boolean.TRUE.equals(success);
72+
}
6573
}

api/src/main/java/org/apache/cloudstack/extension/Extension.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424

2525
public interface Extension extends InternalIdentity, Identity {
2626
enum Type {
27-
Orchestrator
27+
Orchestrator,
28+
NetworkOrchestrator
2829
}
2930
enum State {
3031
Enabled, Disabled;

api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@
4848

4949
public interface ExtensionCustomAction extends InternalIdentity, Identity {
5050
enum ResourceType {
51-
VirtualMachine(com.cloud.vm.VirtualMachine.class);
51+
VirtualMachine(com.cloud.vm.VirtualMachine.class),
52+
Network(com.cloud.network.Network.class),
53+
Vpc(com.cloud.network.vpc.Vpc.class);
5254

5355
private final Class<?> clazz;
5456

0 commit comments

Comments
 (0)