def create(self, do_apply, excpts, oper="add"): logger.info(oper, extra={ 'iface': self.settings['ifname'], 'style': IfStateLogging.STYLE_CHG }) logger.debug("ip link add: {}".format(" ".join( "{}={}".format(k, v) for k, v in self.settings.items()))) if do_apply: try: state = self.settings.pop('state', None) ipr.link('add', **(self.settings)) self.idx = next( iter(ipr.link_lookup(ifname=self.settings['ifname'])), None) if not state is None and not self.idx is None: try: ipr.link('set', index=self.idx, state=state) except Exception as err: if not isinstance(err, netlinkerror_classes): raise excpts.add('set', err, state=state) except Exception as err: if not isinstance(err, netlinkerror_classes): raise excpts.add('add', err, **(self.settings)) if not self.ethtool is None: logger.debug("ethtool: {}".format(self.ethtool)) self.set_ethtool_state(self.settings['ifname'], self.ethtool.keys(), do_apply)
def apply(self, do_apply): excpts = ExceptionCollector(self.settings['ifname']) osettings = copy.deepcopy(self.settings) # lookup for attributes requiring a interface index for attr in self.attr_idx: if attr in self.settings: self.settings[attr] = next( iter(ipr.link_lookup(ifname=self.settings[attr])), self.settings[attr]) if self.idx is None: self.idx = next( iter(ipr.link_lookup(ifname=self.settings['ifname'])), None) if self.idx is not None: self.iface = next(iter(ipr.get_links(self.idx)), None) permaddr = ipr.get_permaddr(self.iface.get_attr('IFLA_IFNAME')) if not permaddr is None: self.iface['permaddr'] = permaddr businfo = ipr.get_businfo(self.iface.get_attr('IFLA_IFNAME')) if not businfo is None: self.iface['businfo'] = businfo # check for ifname collisions idx = next(iter(ipr.link_lookup(ifname=self.settings['ifname'])), None) if idx is not None and idx != self.idx and do_apply: try: ipr.link('set', index=idx, state='down') ipr.link('set', index=idx, ifname='{}!'.format(self.settings['ifname'])) except Exception as err: if not isinstance(err, netlinkerror_classes): raise excpts.add('set', err, state='down', ifname='{}!') if self.cap_create and self.get_if_attr( 'kind') != self.settings['kind']: self.recreate(do_apply, excpts) else: excpts.set_quiet(self.cap_create) self.update(do_apply, excpts) if self.cap_create and excpts.get_all(): excpts.reset() self.recreate(do_apply, excpts) else: self.create(do_apply, excpts) self.settings = osettings return excpts
def recreate(self, do_apply, excpts): logger.debug('has wrong link kind %s, removing', self.settings['kind'], extra={'iface': self.settings['ifname']}) if do_apply: try: ipr.link('del', index=self.idx) except Exception as err: if not isinstance(err, netlinkerror_classes): raise excpts.add('del', err) self.idx = None self.create(do_apply, "replace")
def update(self, do_apply, excpts): logger.debug('checking link', extra={'iface': self.settings['ifname']}) old_state = self.iface['state'] has_link_changes = False has_state_changes = False for setting in self.settings.keys(): logger.debug(' %s: %s => %s', setting, self.get_if_attr(setting), self.settings[setting], extra={'iface': self.settings['ifname']}) if setting == "state": has_state_changes = self.get_if_attr( setting) != self.settings[setting] else: if setting != 'kind' or self.cap_create: has_link_changes |= self.get_if_attr( setting) != self.settings[setting] has_ethtool_changes = set() if not self.ethtool is None: logger.debug('checking ethtool', extra={'iface': self.settings['ifname']}) ethtool = self.get_ethtool_state(self.ethtool.keys()) if ethtool is None: has_ethtool_changes.add(self.ethtool.keys()) else: for setting, options in self.ethtool.items(): if not setting in ethtool: has_ethtool_changes.add(setting) else: for option in options.keys(): logger.debug( ' %s.%s: %s => %s', setting, option, ethtool[setting].get(option), self.ethtool[setting][option], extra={'iface': self.settings['ifname']}) if self.ethtool[setting][option] != ethtool[ setting].get(option): has_ethtool_changes.add(setting) if has_link_changes: logger.debug('needs to be configured', extra={'iface': self.settings['ifname']}) if old_state: logger.debug('shutting down', extra={'iface': self.settings['ifname']}) if do_apply: try: ipr.link('set', index=self.idx, state='down') except Exception as err: if not isinstance(err, netlinkerror_classes): raise excpts.add('set', err, state='down') if not 'state' in self.settings: self.settings['state'] = 'up' self.set_ethtool_state(self.get_if_attr('ifname'), has_ethtool_changes, do_apply) if self.get_if_attr('ifname') == self.settings['ifname']: logger.info('change', extra={ 'iface': self.settings['ifname'], 'style': IfStateLogging.STYLE_CHG }) else: logger.info('change (was {})'.format( self.get_if_attr('ifname')), extra={ 'iface': self.settings['ifname'], 'style': IfStateLogging.STYLE_CHG }) if do_apply: try: state = self.settings.pop('state', None) ipr.link('set', index=self.idx, **(self.settings)) except Exception as err: if not isinstance(err, netlinkerror_classes): raise excpts.add('set', err, state=state) try: if not state is None: # restore state setting for recreate self.settings['state'] = state ipr.link('set', index=self.idx, state=state) except Exception as err: if not isinstance(err, netlinkerror_classes): raise excpts.add('set', err, state=state) else: self.set_ethtool_state(self.get_if_attr('ifname'), has_ethtool_changes, do_apply) if has_state_changes: try: ipr.link('set', index=self.idx, state=self.settings["state"]) except Exception as err: if not isinstance(err, netlinkerror_classes): raise excpts.add('set', err, state=state) logger.info('change', extra={ 'iface': self.settings['ifname'], 'style': IfStateLogging.STYLE_CHG }) else: logger.info('ok', extra={ 'iface': self.settings['ifname'], 'style': IfStateLogging.STYLE_OK })
def _apply(self, do_apply, vrrp_type, vrrp_name, vrrp_state): vrrp_ignore = [] vrrp_remove = [] by_vrrp = not None in [vrrp_type, vrrp_name, vrrp_state] for ifname, link in self.links.items(): if ifname in self.vrrp['links']: if not by_vrrp: vrrp_ignore.append(ifname) else: if not link.match_vrrp_select(vrrp_type, vrrp_name): vrrp_ignore.append(ifname) elif not vrrp_name in self.vrrp[ vrrp_type] or not vrrp_state in self.vrrp[ vrrp_type][ vrrp_name] or not ifname in self.vrrp[ vrrp_type][vrrp_name][vrrp_state]: vrrp_remove.append(ifname) elif by_vrrp: vrrp_ignore.append(ifname) self.ipaddr_ignore = set() for ip in self.ignore.get('ipaddr', []): self.ipaddr_ignore.add(ip_network(ip)) if not any(not x is None for x in self.links.values()): logger.error("DANGER: Not a single link config has been found!") raise LinkNoConfigFound() for iface in ['all', 'default']: if self.sysctl.has_settings(iface): logger.info("\nconfiguring {} interface sysctl".format(iface)) self.sysctl.apply(iface, do_apply) for stage in range(2): if stage == 0: logger.info("\nconfiguring interface links") for ifname in vrrp_remove: logger.debug('to be removed due to vrrp constraint', extra={'iface': ifname}) del self.links[ifname] else: logger.info("\nconfiguring interface links (stage 2)") retry = False applied = [] while len(applied) + len(vrrp_ignore) < len(self.links): last = len(applied) for name, link in self.links.items(): if name in applied: continue if name in vrrp_ignore: logger.debug('skipped due to vrrp constraint', extra={'iface': name}) continue if link is None: logger.debug('skipped due to no link settings', extra={'iface': name}) applied.append(name) else: deps = link.depends() if all(x in applied for x in deps): self.sysctl.apply(name, do_apply) excpts = link.apply(do_apply) if excpts.has_errno(errno.EEXIST): retry = True applied.append(name) if last == len(applied): raise LinkCircularLinked() for link in ipr.get_links(): name = link.get_attr('IFLA_IFNAME') # skip links on ignore list if not name in self.links and not any( re.match(regex, name) for regex in self.ignore.get('ifname', [])): info = link.get_attr('IFLA_LINKINFO') # remove virtual interface if info is not None: kind = info.get_attr('IFLA_INFO_KIND') logger.info('del', extra={ 'iface': name, 'style': IfStateLogging.STYLE_DEL }) if do_apply: try: ipr.link('set', index=link.get('index'), state='down') ipr.link('del', index=link.get('index')) except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning( 'removing link {} failed: {}'.format( name, err.args[1])) # shutdown physical interfaces else: if name in vrrp_ignore: logger.warning('vrrp', extra={ 'iface': name, 'style': IfStateLogging.STYLE_OK }) if link.get('state') == 'down': logger.warning('orphan', extra={ 'iface': name, 'style': IfStateLogging.STYLE_OK }) else: logger.warning('orphan', extra={ 'iface': name, 'style': IfStateLogging.STYLE_CHG }) if do_apply: try: ipr.link('set', index=link.get('index'), state='down') except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning( 'updating link {} failed: {}'.format( name, err.args[1])) if not retry: break if any(not x is None for x in self.tc.values()): logger.info("\nconfiguring interface traffic control...") for name, tc in self.tc.items(): if name in vrrp_ignore: logger.debug('skipped due to vrrp constraint', extra={'iface': name}) elif tc is None: logger.debug('skipped due to no tc settings', extra={'iface': name}) else: tc.apply(do_apply) if any(not x is None for x in self.addresses.values()): logger.info("\nconfiguring interface ip addresses...") # add empty objects for unhandled interfaces for link in ipr.get_links(): name = link.get_attr('IFLA_IFNAME') # skip links on ignore list if not name in self.addresses and not any( re.match(regex, name) for regex in self.ignore.get('ifname', [])): self.addresses[name] = Addresses(name, []) for name, addresses in self.addresses.items(): if name in vrrp_ignore: logger.debug('skipped due to vrrp constraint', extra={'iface': name}) elif addresses is None: logger.debug('skipped due to no address settings', extra={'iface': name}) else: addresses.apply(self.ipaddr_ignore, self.ignore.get('ipaddr_dynamic', True), do_apply) else: logger.info("\nno interface ip addressing to be applied") if any(not x is None for x in self.neighbours.values()): logger.info("\nconfiguring interface neighbours...") # add empty objects for unhandled interfaces for link in ipr.get_links(): name = link.get_attr('IFLA_IFNAME') # skip links on ignore list if not name in self.neighbours and not any( re.match(regex, name) for regex in self.ignore.get('ifname', [])): self.neighbours[name] = Neighbours(name, []) for name, neighbours in self.neighbours.items(): if name in vrrp_ignore: logger.debug('skipped due to vrrp constraint', extra={'iface': name}) elif neighbours is None: logger.debug('skipped due to no address settings', extra={'iface': name}) else: neighbours.apply(do_apply) else: logger.info("\nno interface neighbours to be applied") if not self.tables is None: self.tables.apply(self.ignore.get('routes', []), do_apply) if not self.rules is None: self.rules.apply(self.ignore.get('rules', []), do_apply) if len(self.wireguard): logger.info("\nconfiguring WireGuard...") for iface, wireguard in self.wireguard.items(): if iface in vrrp_ignore: logger.debug('skipped due to vrrp constraint', extra={'iface': name}) continue wireguard.apply(do_apply)