2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-07-21 00:27:28 +00:00

Add net.extra_settings for passthrough network settings

Allow arbitrary per-connection network settings, such as static routes
or a firewalld zone, to be specified as semicolon-delimited key=value
pairs on a net.*.extra_settings attribute. The keys are passed through
to the network backend of the deployed OS in its native syntax: nmcli
properties on NetworkManager systems, netplan YAML paths on netplan
systems, and ifcfg variables on wicked systems.
This commit is contained in:
Markus Hilger
2026-07-11 06:35:56 +02:00
parent 0272137e94
commit 2f006e507f
5 changed files with 87 additions and 0 deletions
@@ -130,6 +130,21 @@ to a blank value will allow masking a group defined attribute with an empty valu
`node12: net.compute.ipv4_address: 172.17.0.12/16`
`node12: net.compute.team_mode: lacp`
* Passing additional settings to the network backend of the deployed OS with `net.extra_settings`
(semicolon-delimited key=value pairs, keys in the native syntax of the respective backend).
On a NetworkManager based OS (e.g. Enterprise Linux), keys are nmcli properties:
`# nodeattrib node12 net.mgmt.extra_settings='connection.zone=internal;ipv4.routes=10.0.0.0/8 192.168.1.254, 172.16.0.0/12 192.168.1.254;ipv4.route-metric=200'`
`node12: net.mgmt.extra_settings: connection.zone=internal;ipv4.routes=10.0.0.0/8 192.168.1.254, 172.16.0.0/12 192.168.1.254;ipv4.route-metric=200`
* On a netplan based OS (e.g. Ubuntu), keys are netplan YAML paths (nested keys dotted, values in YAML flow syntax).
Since Confluent uses braces for attribute expressions, literal braces must be escaped as `{{` and `}}` when setting the attribute:
`# nodeattrib node13 net.mgmt.extra_settings='routes=[{{to: 10.0.0.0/8, via: 192.168.1.254}}];nameservers.search=[lab.example.com]'`
`node13: net.mgmt.extra_settings: routes=[{to: 10.0.0.0/8, via: 192.168.1.254}];nameservers.search=[lab.example.com]`
* On a wicked based OS (e.g. SUSE), keys are ifcfg variables (routes are not supported through this mechanism on wicked):
`# nodeattrib node14 net.mgmt.extra_settings='ZONE=internal;ETHTOOL_OPTIONS=-K iface tso off'`
`node14: net.mgmt.extra_settings: ZONE=internal;ETHTOOL_OPTIONS=-K iface tso off`
* Clear attribute on nodes of a simple noderange, if you want to retain the variable set the attribute to "":
`# nodeattrib n1-n2 -c console.method`
`# nodeattrib n1-n2 console.method`
@@ -114,6 +114,14 @@ def get_interface_name(iname, settings):
return iname
return None
def parse_extra_settings(stgs):
extras = {}
for kv in stgs.get('extra_settings', '').split(';'):
k, _, v = kv.partition('=')
if k.strip():
extras[k.strip()] = v.strip()
return extras
class NetplanManager(object):
def __init__(self, deploycfg):
self.cfgbydev = {}
@@ -229,6 +237,24 @@ class NetplanManager(object):
if dnsdomain not in currdnsdomain:
needcfgwrite = True
currdnsdomain.append(dnsdomain)
extras = parse_extra_settings(stgs)
if extras:
currcfg = self.cfgbybond if devname in self.cfgbybond else self.cfgbydev
devdict = currcfg.setdefault(devname, {})
for key in extras:
try:
val = yaml.safe_load(extras[key])
except yaml.YAMLError:
val = extras[key]
keyptr = devdict
keypath = key.split('.')
for k in keypath[:-1]:
if not isinstance(keyptr.get(k, None), dict):
keyptr[k] = {}
keyptr = keyptr[k]
if keyptr.get(keypath[-1], None) != val:
needcfgwrite = True
keyptr[keypath[-1]] = val
prune_from_cloudinit = []
if needcfgwrite:
needcfgapply = True
@@ -336,6 +362,9 @@ class WickedManager(object):
if stgs.get('ipv6_address', None):
ipcfg += 'IPADDR_V6=' + stgs['ipv6_address'] + '\n'
v6gw = stgs.get('ipv6_gateway', None)
extras = parse_extra_settings(stgs)
for key in extras:
ipcfg += '{0}={1}\n'.format(key, shlex.quote(extras[key]))
cname = None
if len(cfg['interfaces']) > 1: # creating new team
if not stgs.get('team_mode', None):
@@ -475,6 +504,7 @@ class NetworkManager(object):
cmdargs['ipv4.dns'] = ','.join(dns4)
if dns6:
cmdargs['ipv6.dns'] = ','.join(dns6)
cmdargs.update(parse_extra_settings(stgs))
if len(cfg['interfaces']) > 1: # team time.. should be..
if not cfg['settings'].get('team_mode', None):
sys.stderr.write("Warning, multiple interfaces ({0}) without a team_mode, skipping setup\n".format(','.join(cfg['interfaces'])))
@@ -111,6 +111,14 @@ def get_interface_name(iname, settings):
return iname
return None
def parse_extra_settings(stgs):
extras = {}
for kv in stgs.get('extra_settings', '').split(';'):
k, _, v = kv.partition('=')
if k.strip():
extras[k.strip()] = v.strip()
return extras
class NetplanManager(object):
def __init__(self, deploycfg):
self.cfgbydev = {}
@@ -192,6 +200,23 @@ class NetplanManager(object):
if dnsdomain not in currdnsdomain:
needcfgwrite = True
currdnsdomain.append(dnsdomain)
extras = parse_extra_settings(stgs)
if extras:
devdict = self.cfgbydev.setdefault(devname, {})
for key in extras:
try:
val = yaml.safe_load(extras[key])
except yaml.YAMLError:
val = extras[key]
keyptr = devdict
keypath = key.split('.')
for k in keypath[:-1]:
if not isinstance(keyptr.get(k, None), dict):
keyptr[k] = {}
keyptr = keyptr[k]
if keyptr.get(keypath[-1], None) != val:
needcfgwrite = True
keyptr[keypath[-1]] = val
if needcfgwrite:
needcfgapply = True
newcfg = {'network': {'version': 2, 'ethernets': {devname: self.cfgbydev[devname]}}}
@@ -261,6 +286,9 @@ class WickedManager(object):
if stgs.get('ipv6_address', None):
ipcfg += 'IPADDR_V6=' + stgs['ipv6_address'] + '\n'
v6gw = stgs.get('ipv6_gateway', None)
extras = parse_extra_settings(stgs)
for key in extras:
ipcfg += '{0}={1}\n'.format(key, shlex.quote(extras[key]))
cname = None
if len(cfg['interfaces']) > 1: # creating new team
if not stgs.get('team_mode', None):
@@ -400,6 +428,7 @@ class NetworkManager(object):
cmdargs['ipv4.dns'] = ','.join(dns4)
if dns6:
cmdargs['ipv6.dns'] = ','.join(dns6)
cmdargs.update(parse_extra_settings(stgs))
if len(cfg['interfaces']) > 1: # team time.. should be..
if not cfg['settings'].get('team_mode', None):
sys.stderr.write("Warning, multiple interfaces ({0}) without a team_mode, skipping setup\n".format(','.join(cfg['interfaces'])))
@@ -589,6 +589,16 @@ node = {
'To support this scenario, the switch should be set up to allow independent operation of member ports (e.g. lacp bypass mode or fallback mode).',
'validvalues': ('lacp', 'loadbalance', 'roundrobin', 'activebackup', 'none')
},
'net.extra_settings': {
'description': 'Additional network settings to apply to the connection, as '
'semicolon-delimited key=value pairs (e.g. '
'"connection.zone=internal;ipv4.routes=10.0.0.0/8 192.168.1.254"). '
'The keys are passed through to the network configuration backend of '
'the deployed OS and use its native syntax: nmcli property names on '
'NetworkManager based systems, netplan YAML keys (values in YAML flow '
'syntax, nested keys dotted) on netplan based systems, or ifcfg '
'variables on wicked based systems.',
},
'power.pdu': {
'description': 'Specifies the managed PDU associated with a power input on the node'
},
+3
View File
@@ -246,6 +246,9 @@ class NetManager(object):
teammod = attribs.get('team_mode', None)
if teammod:
myattribs['team_mode'] = teammod
extrastgs = attribs.get('extra_settings', None)
if extrastgs:
myattribs['extra_settings'] = extrastgs
method = attribs.get('ipv4_method', None)
if method != 'dhcp':
ipv4addr = attribs.get('ipv4_address', None)