feat(opennebula): support ETHx_ALIASn_IP/MASK for anycast addresses#6876
feat(opennebula): support ETHx_ALIASn_IP/MASK for anycast addresses#6876mcanevet wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR extends the OpenNebula datasource’s network configuration generation to include per-NIC alias (anycast/VIP) IPv4 addresses defined via ETH<x>_ALIAS<n>_IP / ETH<x>_ALIAS<n>_MASK, so they appear in the Netplan v2 addresses list alongside the primary address (defaulting missing masks to /32).
Changes:
- Add
OpenNebulaNetwork.get_alias_addresses()to collect alias IPv4 addresses from context. - Extend
OpenNebulaNetwork.gen_conf()to append alias addresses into Netplanaddresses. - Add unit tests and update OpenNebula datasource documentation for the new context variables.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
cloudinit/sources/DataSourceOpenNebula.py |
Implements alias address parsing and includes aliases in generated Netplan output. |
tests/unittests/sources/test_opennebula.py |
Adds unit tests covering alias parsing and integration into gen_conf(). |
doc/rtd/reference/datasources/opennebula.rst |
Documents ETH<x>_ALIAS<n>_IP/MASK behavior and default mask handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
23811d2 to
16aff3a
Compare
blackboxsw
left a comment
There was a problem hiding this comment.
Thank you again @mcanevet for this proposal. I think we might want a bit more logging verbosity when cloud-init ignores potential invalid config values such as gap ranges in aliases. Silently ignoring potential config is not ideal.
Can you please provide the representative /etc/netplan/50-cloud-init.yaml created and cloud-init status --format=yaml on a vm with this context provided if possible?
|
Thanks for this PR, I can confirm the documented behavior and contextualization switches documented in opennebula 7.2 operations guide](https://docs.opennebula.io/7.2/product/operation_references/configuration_references/template/#template-context) |
OpenNebula's context-linux package supports per-NIC alias addresses (ETHx_ALIAS0_IP, ETHx_ALIAS1_IP, …) used for anycast IPs. Add get_alias_addresses() to OpenNebulaNetwork and wire it into gen_conf() so alias addresses appear in the Netplan v2 output alongside the primary address. Missing MASK defaults to /32.
get_alias_addresses() silently ignored any alias entries beyond a gap in the index sequence (e.g. ALIAS0 and ALIAS2 present but ALIAS1 missing), giving no indication that context was likely misconfigured. Now log a warning naming the skipped keys.
16aff3a to
2373576
Compare
|
@blackboxsw fixed |
blackboxsw
left a comment
There was a problem hiding this comment.
Thank you for the swift updates @mcanevet. I'm wondering if these is a better way to simplify the separate ordered looping when detecting gaps. Can we consolidate some of that looping somehow? Given that you are using a regex below, that regex could help in a for loop instead of just using a while loop and an incrementing idx to encounter a a gap in self.context.
| "Ignoring %s: found gap at %s%d_IP", | ||
| ", ".join("%s%d_IP" % (prefix, i) for i in skipped), |
There was a problem hiding this comment.
gap doesn't provide enough context in the message. Let's be a bit more explicit about the problem.
Ignoring network config keys %s due to missing %s
| idx = 0 | ||
| while True: | ||
| ip_key = "%s%d_IP" % (prefix, idx) | ||
| ip = self.context.get(ip_key) | ||
| if not ip: | ||
| break | ||
| mask_key = "%s%d_MASK" % (prefix, idx) | ||
| mask = self.context.get(mask_key) or "255.255.255.255" | ||
| net_prefix = str(net.ipv4_mask_to_net_prefix(mask)) | ||
| aliases.append("%s/%s" % (ip, net_prefix)) | ||
| idx += 1 | ||
|
|
||
| key_re = re.compile(r"^%s(\d+)_IP$" % re.escape(prefix)) | ||
| skipped = sorted( | ||
| int(m.group(1)) for m in map(key_re.match, self.context) if m | ||
| ) | ||
| skipped = [i for i in skipped if i >= idx] |
There was a problem hiding this comment.
It feels a bit like we are processing the data in self.context twice through two separate loops which makes it a teeny bit harder to maintain when we look at this in the future.
Could we instead try processing these keys once?
something like (completely untested):
key_re = re.compile(rf"^{c_dev.upper()}_ALIAS(?P<alias_idx>\d+)_IP$" % re.escape(prefix))
for key, value in sorted(self.content.items()):
match = key_re.match(key)
if not match
continue
if int(match['alias_idx']) == idx:
idx += 1
mask_key = self.context.get(key.replace("IP", "MASK"))
mask = self.context.key(mask_key) or "255.255.255.255"
aliases.append(f"{value}/{mask}")
else:
skipped.append(key)
Proposed Commit Message
```
feat(opennebula): support ETHx_ALIASn_IP/MASK for anycast addresses
OpenNebula's context-linux package supports per-NIC alias addresses
(ETHx_ALIAS0_IP, ETHx_ALIAS1_IP, …) used for anycast IPs. Add
get_alias_addresses() to OpenNebulaNetwork and wire it into gen_conf()
so alias addresses appear in the Netplan v2 output alongside the
primary address. Missing MASK defaults to /32.
```
Additional Context
OpenNebula allows attaching multiple IP addresses to a single NIC via
ETH<x>_ALIAS<n>_IP/ETH<x>_ALIAS<n>_MASKcontext variables. Theseare typically used for anycast or VIP addresses. Previously cloud-init
ignored them, leaving the addresses unconfigured on the guest.
Test Steps
```
ETH0_ALIAS0_IP = "192.168.1.10"
ETH0_ALIAS0_MASK = "255.255.255.0"
ETH0_ALIAS1_IP = "192.168.1.11"
```
```
ip addr show eth0
expected: 192.168.1.10/24 and 192.168.1.11/32 present
```Merge type