From abd2da5777195e7e432b0d53a3f7f29d071dd50e Mon Sep 17 00:00:00 2001 From: James Falcon Date: Thu, 17 Jun 2021 16:44:55 -0500 Subject: Fix DNS in NetworkState (SC-133) (#923) v1 network config currently has no concept of interface-specific DNS, which is required for certain renderers. To fix this, added an optional 'interface' key on the v1 nameserver definition. If specified, it makes the DNS settings specific to the interface. Otherwise, it will be defined as global DNS as it always has. Additionally, DNS for v2 wasn't being recognized correctly. For DNS defined on a particular interface, these settings now also go into the global DNS settings as they were intended. --- cloudinit/net/network_state.py | 66 ++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 15 deletions(-) (limited to 'cloudinit/net/network_state.py') diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py index e8bf9e39..8018cfb9 100644 --- a/cloudinit/net/network_state.py +++ b/cloudinit/net/network_state.py @@ -237,6 +237,7 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta): self._network_state = copy.deepcopy(self.initial_network_state) self._network_state['config'] = config self._parsed = False + self._interface_dns_map = {} @property def network_state(self): @@ -310,6 +311,21 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta): LOG.warning("Skipping invalid command: %s", command, exc_info=True) LOG.debug(self.dump_network_state()) + for interface, dns in self._interface_dns_map.items(): + iface = None + try: + iface = self._network_state['interfaces'][interface] + except KeyError as e: + raise ValueError( + 'Nameserver specified for interface {0}, ' + 'but interface {0} does not exist!'.format(interface) + ) from e + if iface: + nameservers, search = dns + iface['dns'] = { + 'addresses': nameservers, + 'search': search, + } def parse_config_v2(self, skip_broken=True): for command_type, command in self._config.items(): @@ -526,21 +542,40 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta): def handle_infiniband(self, command): self.handle_physical(command) - @ensure_command_keys(['address']) - def handle_nameserver(self, command): - dns = self._network_state.get('dns') + def _parse_dns(self, command): + nameservers = [] + search = [] if 'address' in command: addrs = command['address'] if not type(addrs) == list: addrs = [addrs] for addr in addrs: - dns['nameservers'].append(addr) + nameservers.append(addr) if 'search' in command: paths = command['search'] if not isinstance(paths, list): paths = [paths] for path in paths: - dns['search'].append(path) + search.append(path) + return nameservers, search + + @ensure_command_keys(['address']) + def handle_nameserver(self, command): + dns = self._network_state.get('dns') + nameservers, search = self._parse_dns(command) + if 'interface' in command: + self._interface_dns_map[command['interface']] = ( + nameservers, search + ) + else: + dns['nameservers'].extend(nameservers) + dns['search'].extend(search) + + @ensure_command_keys(['address']) + def _handle_individual_nameserver(self, command, iface): + _iface = self._network_state.get('interfaces') + nameservers, search = self._parse_dns(command) + _iface[iface]['dns'] = {'nameservers': nameservers, 'search': search} @ensure_command_keys(['destination']) def handle_route(self, command): @@ -706,16 +741,17 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta): def _v2_common(self, cfg): LOG.debug('v2_common: handling config:\n%s', cfg) - if 'nameservers' in cfg: - search = cfg.get('nameservers').get('search', []) - dns = cfg.get('nameservers').get('addresses', []) - name_cmd = {'type': 'nameserver'} - if len(search) > 0: - name_cmd.update({'search': search}) - if len(dns) > 0: - name_cmd.update({'addresses': dns}) - LOG.debug('v2(nameserver) -> v1(nameserver):\n%s', name_cmd) - self.handle_nameserver(name_cmd) + for iface, dev_cfg in cfg.items(): + if 'nameservers' in dev_cfg: + search = dev_cfg.get('nameservers').get('search', []) + dns = dev_cfg.get('nameservers').get('addresses', []) + name_cmd = {'type': 'nameserver'} + if len(search) > 0: + name_cmd.update({'search': search}) + if len(dns) > 0: + name_cmd.update({'address': dns}) + self.handle_nameserver(name_cmd) + self._handle_individual_nameserver(name_cmd, iface) def _handle_bond_bridge(self, command, cmd_type=None): """Common handler for bond and bridge types""" -- cgit v1.2.3