diff options
| -rw-r--r-- | cloudinit/config/cc_vyos.py | 78 |
1 files changed, 63 insertions, 15 deletions
diff --git a/cloudinit/config/cc_vyos.py b/cloudinit/config/cc_vyos.py index 56014794..1e265c4d 100644 --- a/cloudinit/config/cc_vyos.py +++ b/cloudinit/config/cc_vyos.py @@ -36,6 +36,9 @@ from cloudinit.util import load_file, load_json, get_hostname_fqdn, get_cfg_by_p from cloudinit.sources.DataSourceOVF import get_properties as ovf_get_properties try: from vyos.configtree import ConfigTree + from vyos.ifconfig.ifname_store import discover_devices + from vyos.ifconfig.ifname_store import load_store + from vyos.ifconfig.ifname_store import save_store except ImportError as err: print(f'The module cannot be imported: {err}') @@ -466,12 +469,6 @@ def set_config_interfaces_v1(config, iface_config: dict): if iface_config['type'] == 'physical': iface_name = iface_config['name'] - # configre MAC - if 'mac_address' in iface_config: - logger.debug("Setting MAC for {}: {}".format(iface_name, iface_config['mac_address'])) - config.set(['interfaces', 'ethernet', iface_name, 'hw-id'], value=iface_config['mac_address'], replace=True) - config.set_tag(['interfaces', 'ethernet']) - # configre MTU if 'mtu' in iface_config and iface_config['mtu'] is not None: set_ether_mtu(config, iface_name, iface_config['mtu']) @@ -955,15 +952,6 @@ def config_net_v2_ethernet(config, iface_name: str, iface_config: dict) -> None: config_net_v2_common(config, 'ethernet', iface_name, iface_config) - # configure MAC - if 'match' in iface_config and 'macaddress' in iface_config['match']: - logger.debug("Setting MAC for {}: {}".format( - iface_name, iface_config['match']['macaddress'])) - config.set(['interfaces', 'ethernet', iface_name, 'hw-id'], - value=iface_config['match']['macaddress'], - replace=True) - config.set_tag(['interfaces', 'ethernet']) - # configure interface from networking config version 2 def set_config_interfaces_v2(config, netcfg: dict) -> None: @@ -979,6 +967,64 @@ def set_config_interfaces_v2(config, netcfg: dict) -> None: config_net_v2_vlan(config, iface_name, iface_config) +# record the names the cloud gave its NICs +def record_interface_mapping(netcfg: dict) -> None: + """T3871: a name is anchored to the slot a card sits in, recorded in the + mapping store rather than in the configuration. Which NIC is the first one + is the cloud's to decide and is not visible in the hardware, so the order + the datasource gives has to reach the store before the naming pass reads + it. + """ + bindings = {} + if netcfg.get('version') == 1: + for iface_config in netcfg.get('config', []): + if iface_config.get('type') != 'physical': + continue + if iface_config.get('name') and iface_config.get('mac_address'): + bindings[iface_config['name']] = \ + iface_config['mac_address'].lower() + elif netcfg.get('version') == 2: + for iface_name, iface_config in netcfg.get('ethernets', {}).items(): + mac = iface_config.get('match', {}).get('macaddress') + if mac: + bindings[iface_name] = mac.lower() + + if not bindings: + return + + try: + devices = {device['mac'].lower(): device + for device in discover_devices()} + store = load_store() + except Exception as err: + logger.error("Failed to read the interface mapping: {}".format(err)) + return + + entries = store['interfaces'] + taken = set(entries.values()) + added = False + + for iface_name, mac in bindings.items(): + device = devices.get(mac) + # a NIC the datasource describes but which is not here has nothing to + # anchor a name to + if device is None or not device['key']: + logger.debug("No hardware for {} ({})".format(iface_name, mac)) + continue + # an entry already in the store is authoritative, and a slot already + # spoken for must not gain a second name + if iface_name in entries or device['key'] in taken: + continue + entries[iface_name] = device['key'] + store['hardware'][iface_name] = mac + taken.add(device['key']) + added = True + logger.debug("Mapping {} to {}".format(iface_name, device['key'])) + + if added and not save_store(store): + logger.error("Failed to save the interface mapping") + + # configure SSH server service def set_config_ssh(config): logger.debug("Configuring SSH service") @@ -1131,6 +1177,8 @@ def handle(name, cfg, cloud, log, _args): # process networking configuration data if netcfg and network_configured is False and network_config_vyos != 'disabled': + record_interface_mapping(netcfg) + # check which one version of config we have # version 1 if netcfg['version'] == 1: |
