Skip to content

Commit fc70a24

Browse files
committed
NE: add network.allocate.extension.ip to control if allocate a placeholder NIC
1 parent 0bde5d4 commit fc70a24

4 files changed

Lines changed: 73 additions & 15 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
public interface ExtensionHelper {
2727
Long getExtensionIdForCluster(long clusterId);
2828
Extension getExtension(long id);
29+
Extension getExtensionByNameAndType(String name, Extension.Type type);
2930
Extension getExtensionForCluster(long clusterId);
3031
List<String> getExtensionReservedResourceDetails(long extensionId);
3132

framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/manager/ExtensionsManagerImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,6 +2222,11 @@ public Extension getExtension(long id) {
22222222
return extensionDao.findById(id);
22232223
}
22242224

2225+
@Override
2226+
public Extension getExtensionByNameAndType(String name, Extension.Type type) {
2227+
return extensionDao.findByNameAndType(name, type);
2228+
}
2229+
22252230
@Override
22262231
public Extension getExtensionForCluster(long clusterId) {
22272232
Long extensionId = getExtensionIdForCluster(clusterId);

framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/network/NetworkExtensionElement.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
import org.apache.cloudstack.extension.ExtensionHelper;
123123
import org.apache.cloudstack.extension.NetworkCustomActionProvider;
124124
import org.apache.cloudstack.framework.extensions.dao.ExtensionDetailsDao;
125+
import org.apache.cloudstack.framework.extensions.vo.ExtensionDetailsVO;
125126
import org.apache.cloudstack.resourcedetail.dao.VpcDetailsDao;
126127
import org.apache.commons.collections.CollectionUtils;
127128
import org.apache.commons.collections.MapUtils;
@@ -256,6 +257,21 @@ public class NetworkExtensionElement extends AdapterBase implements
256257
*/
257258
public static final String NETWORK_DETAIL_EXTENSION_DETAILS = "extension.details";
258259

260+
/**
261+
* Extension detail key that controls whether {@link #ensureExtensionIp} allocates
262+
* a placeholder NIC/IP.
263+
*
264+
* <p>Applies to any network whose DHCP/DNS/UserData service is provided by this
265+
* extension <em>without</em> SourceNat or Gateway — i.e. shared networks and
266+
* isolated networks that omit SourceNat. Set the value to {@code "false"} on the
267+
* Extension object (via {@code createExtension} or {@code updateExtension}) when
268+
* the extension handles its own data-plane addressing and does not need CloudStack
269+
* to reserve a real IP from the subnet pool (e.g. OVN, which programs port
270+
* bindings in-fabric). Omitting the detail or setting any value other than
271+
* {@code "false"} preserves the original behaviour (placeholder IP is allocated).</p>
272+
*/
273+
public static final String NETWORK_ALLOCATE_EXTENSION_IP = "network.allocate.extension.ip";
274+
259275
public String getProviderName() {
260276
return providerName;
261277
}
@@ -677,6 +693,17 @@ protected String ensureExtensionIp(Network network) {
677693

678694
if (networkModel.isAnyServiceSupportedInNetwork(network.getId(), this.getProvider(),
679695
Service.Dhcp, Service.Dns, Service.UserData)) {
696+
// Some extensions (e.g. OVN) handle shared-network addressing in the
697+
// data plane and do not need a CloudStack-allocated placeholder IP.
698+
Extension ext = extensionHelper.getExtensionByNameAndType(providerName, Extension.Type.NetworkOrchestrator);
699+
if (ext != null) {
700+
ExtensionDetailsVO allocateDetail =
701+
extensionDetailsDao.findDetail(ext.getId(), NETWORK_ALLOCATE_EXTENSION_IP);
702+
if (allocateDetail != null && "false".equalsIgnoreCase(allocateDetail.getValue())) {
703+
logger.debug("Skipping placeholder IP for network {} — {} detail is false",
704+
network.getId(), NETWORK_ALLOCATE_EXTENSION_IP);
705+
return null;
706+
}
680707
try {
681708
// An extra IP will be allocated and configured on the external network
682709
Nic placeholderNic = networkModel.getPlaceholderNicForRouter(network, null);
@@ -685,7 +712,7 @@ protected String ensureExtensionIp(Network network) {
685712
String routerIp = routerIpDetail != null ? routerIpDetail.getValue() : null;
686713
Account account = accountService.getAccount(network.getAccountId());
687714
String extensionIp = Network.GuestType.Shared.equals(network.getGuestType()) ?
688-
ipAddressManager.assignPublicIpAddress(network.getDataCenterId(), null, account, Vlan.VlanType.DirectAttached, network.getId(), routerIp, false, false).getAddress().toString():
715+
ipAddressManager.assignPublicIpAddress(network.getDataCenterId(), null, account, Vlan.VlanType.DirectAttached, network.getId(), routerIp, false, false).getAddress().toString() :
689716
ipAddressManager.acquireGuestIpAddress(network, routerIp);
690717
logger.debug("Saving placeholder nic with ip4 address {} for the network", extensionIp, network);
691718
networkManager.savePlaceholderNic(network, extensionIp, null, VirtualMachine.Type.DomainRouter);
@@ -695,6 +722,7 @@ protected String ensureExtensionIp(Network network) {
695722
} catch (Exception e) {
696723
logger.warn("Failed to acquire extension IP for network {}: {}", network, e.getMessage());
697724
}
725+
}
698726
}
699727
return null;
700728
}

framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/network/README.md

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,10 @@ registration as extension metadata. The `custom-action` path embeds them
188188
directly into the payload file under `physical-network-extension-details`.
189189
The schema is entirely yours — CloudStack treats it as opaque.
190190

191-
> **`isolation_method=NetworkExtension`** tells CloudStack to select
192-
> `NetworkExtensionGuestNetworkGuru` when designing guest networks backed by
193-
> this extension. This is required whenever your `implement-network` handler
194-
> outputs JSON that updates the network's broadcast domain type (e.g.
195-
> `"network.broadcast_domain_type": "Lswitch"` for OVN-backed extensions).
191+
> **`network.isolation.method=NetworkExtension`** must be set as an Extension
192+
> detail (via `createExtension` or `updateExtension`).
193+
> This is required whenever your `implement-network` handler outputs JSON that
194+
> updates the network's broadcast domain type.
196195
> Without it the script output is accepted but silently ignored — the network
197196
> record in the CloudStack database is not updated.
198197
@@ -391,14 +390,14 @@ configure the gateway.
391390
> CloudStack now also includes `network_ip6_gateway` and `network_ip6_cidr`
392391
> when the guest network has IPv6 configured.
393392
394-
**Stdout (required when `isolation_method=NetworkExtension`):**
393+
**Stdout (required when `network.isolation.method=NetworkExtension`):**
395394

396-
When the physical-network registration detail `isolation_method=NetworkExtension`
397-
is set, CloudStack selects `NetworkExtensionGuestNetworkGuru` and applies the
398-
JSON object printed to stdout back to the network record. The following two
399-
fields **must** be present in the output so that CloudStack stores the correct
400-
broadcast type and URI — without them the KVM agent (`OvsVifDriver`) will not
401-
set `external_ids:iface-id` on the OVS tap port and OVN port-binding will fail:
395+
When the Extension detail `network.isolation.method=NetworkExtension` is set,
396+
CloudStack selects `NetworkExtensionGuestNetworkGuru` and applies the JSON object
397+
printed to stdout back to the network record. The following two fields **must**
398+
be present in the output so that CloudStack stores the correct broadcast type and
399+
URI — without them the KVM agent (`OvsVifDriver`) will not set
400+
`external_ids:iface-id` on the OVS tap port and OVN port-binding will fail:
402401

403402
| Output key | Required value | Description |
404403
|---|---|---|
@@ -411,8 +410,8 @@ Example stdout:
411410
{"network.broadcast_domain_type": "Lswitch", "network.broadcast_uri": "ovn://cs-net-42"}
412411
```
413412

414-
> **Note:** These fields are only consumed when `isolation_method=NetworkExtension`
415-
> is set on the physical-network registration. Without that detail, the script
413+
> **Note:** These fields are only consumed when the Extension detail
414+
> `network.isolation.method=NetworkExtension` is set. Without it, the script
416415
> output is accepted but the network record is **not** updated (the update is
417416
> silently skipped).
418417
@@ -1260,6 +1259,31 @@ To use this extension as a VPC provider:
12601259
it. The device must listen on this IP for DHCP, DNS, and metadata (port 80)
12611260
requests.
12621261

1262+
### Opting out of the placeholder IP
1263+
1264+
Some extensions (e.g. OVN) manage their own data-plane addressing and do not
1265+
need CloudStack to reserve a real IP for networks that provide DHCP/DNS/UserData
1266+
without SourceNat or Gateway. This covers **both** shared networks and isolated
1267+
networks that omit SourceNat. Set the Extension detail
1268+
**`network.allocate.extension.ip=false`** when creating or updating the Extension
1269+
to suppress the allocation:
1270+
1271+
```bash
1272+
cmk createExtension \
1273+
name=my-sdn \
1274+
... \
1275+
"details[N].key=network.allocate.extension.ip" "details[N].value=false"
1276+
```
1277+
1278+
When this detail is `false`, `ensureExtensionIp` returns `null` for any network
1279+
backed by this extension that reaches the placeholder-IP branch — i.e. networks
1280+
with DHCP/DNS/UserData but without SourceNat/Gateway. `extension_ip` will not
1281+
be set in command payloads, and no IP is consumed from the subnet pool.
1282+
1283+
Omitting the detail (or setting any value other than `false`) preserves the
1284+
original behaviour — a placeholder NIC is allocated and `extension_ip` is
1285+
included in every command payload.
1286+
12631287
---
12641288

12651289
## Exit Codes

0 commit comments

Comments
 (0)