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 __init__(self, name, link, ethtool, vrrp): self.cap_create = True self.cap_ethtool = False self.settings = { 'ifname': name, } self.settings.update(link) self.ethtool = None self.vrrp = vrrp self.attr_map = { 'kind': ['IFLA_LINKINFO', 'IFLA_INFO_KIND'], } self.attr_idx = [ 'link', 'master', 'gre_link', 'ip6gre_link', 'vxlan_link', 'xfrm_link' ] self.idx = None if 'address' in self.settings: self.settings['address'] = self.settings['address'].lower() self.idx = next( iter(ipr.link_lookup(address=self.settings['address'])), None) if 'permaddr' in self.settings: self.settings['permaddr'] = self.settings['permaddr'].lower() self.idx = ipr.get_iface_by_permaddr(self.settings['permaddr']) if 'businfo' in self.settings: self.settings['businfo'] = self.settings['businfo'].lower() self.idx = ipr.get_iface_by_businfo(self.settings['businfo']) for attr, mappings in self.attr_value_maps.items(): if attr in self.settings and type(self.settings[attr]) != int: self.settings[attr] = next( (k for k, v in mappings.items() if v == self.settings[attr]), self.settings[attr])
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(ifname=self.iface) # get ifindex self.idx = next(iter(ipr.link_lookup(ifname=self.iface)), None) if self.idx == None: logger.warning('link missing', extra={'iface': self.iface}) return changes = [] ipr_qdiscs = None # apply ingress qdics if "ingress" in self.tc: ipr_qdiscs = ipr.get_qdiscs(index=self.idx) if self.apply_ingress(self.tc["ingress"], self.get_qdisc( ipr_qdiscs, TC.INGRESS_PARENT), excpts, do_apply): changes.append("ingress") # apply qdisc tree if "qdisc" in self.tc: if ipr_qdiscs is None: ipr_qdiscs = ipr.get_qdiscs(index=self.idx) logger.debug('checking qdisc tree', extra={'iface': self.iface}) if self.apply_qtree( self.tc["qdisc"], self.get_qdisc(ipr_qdiscs, TC.ROOT_HANDLE), ipr_qdiscs, TC.ROOT_HANDLE, excpts, do_apply): changes.append("qdisc") # apply filters if "filter" in self.tc: ipr_filters = ipr.get_filters( index=self.idx) + ipr.get_filters(index=self.idx, parent=TC.INGRESS_HANDLE) logger.debug('checking filters', extra={'iface': self.iface}) if self.apply_filter( self.tc["filter"], ipr_filters, excpts, do_apply): changes.append("filter") if len(changes) > 0: logger.info( 'change ({})'.format(", ".join(changes)), extra={'iface': self.iface, 'style': IfStateLogging.STYLE_CHG}) else: logger.info( 'ok', extra={'iface': self.iface, 'style': IfStateLogging.STYLE_OK}) return excpts
def apply(self, do_apply): logger.debug('getting neighbours', extra={'iface': self.iface}) # get ifindex idx = next(iter(ipr.link_lookup(ifname=self.iface)), None) if idx == None: logger.warning('link missing', extra={'iface': self.iface}) return # get neighbour entries (only NUD_PERMANENT) ipr_neigh = {} neigh_add = {} for neigh in ipr.get_neighbours(ifindex=idx, state=128): ip = ip_address(neigh.get_attr('NDA_DST')) ipr_neigh[ip] = neigh.get_attr('NDA_LLADDR') for ip, lladdr in self.neighbours.items(): if ip in ipr_neigh and lladdr == ipr_neigh[ip]: logger.info(' %s', ip, extra={ 'iface': self.iface, 'style': IfStateLogging.STYLE_OK}) del ipr_neigh[ip] else: neigh_add[ip] = lladdr for ip, lladdr in ipr_neigh.items(): logger.info( '-%s', str(ip), extra={'iface': self.iface, 'style': IfStateLogging.STYLE_DEL}) try: if do_apply: ipr.neigh("del", ifindex=idx, dst=str( ip)) except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning('removing neighbour {} failed: {}'.format( str(ip), err.args[1])) for ip, lladdr in neigh_add.items(): logger.info('+%s', str(ip), extra={'iface': self.iface, 'style': IfStateLogging.STYLE_CHG}) if do_apply: try: opts = { 'ifindex': idx, 'dst': str(ip), 'lladdr': lladdr, 'state': 128 } ipr.neigh('replace', **opts) except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning('adding neighbour {} failed: {}'.format( str(ip), err.args[1]))
def apply_filter(self, tc, ipr_filters, excpts, do_apply): tc_filters = {} # assign prio numbers if missing for i in range(len(tc)): if not "prio" in tc[i]: tc[i]["prio"] = 0xc001 - len(tc) + i parent = TC.handle2int(tc[i].get("parent", 0)) if not parent in tc_filters: tc_filters[parent] = {} tc_filters[parent][tc[i]["prio"]] = tc[i] changes = False # remove unreferenced filters removed = {} for ipr_filter in ipr_filters: prio = ipr_filter["info"] >> 16 parent = TC.handle2int(ipr_filter.get("parent", 0)) rm = not parent in tc_filters rm |= parent in tc_filters and \ prio not in tc_filters[parent] if rm and prio not in removed.get(parent, []): changes = True if not parent in removed: removed[parent] = [] removed[parent].append(prio) if do_apply: opts = { "index": self.idx, "info": ipr_filter["info"], "parent": parent, } try: ipr.del_filter_by_info(**opts) except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning('deleting filter #{} on {} failed: {}'.format( prio, self.iface, err.args[1])) excpts.add('del', err, **opts) if do_apply: for parent in tc_filters.keys(): for tc_filter in tc_filters[parent].values(): tc_filter['index'] = self.idx if "action" in tc_filter: for action in tc_filter["action"]: if action["kind"] == "mirred": # get ifindex action["ifindex"] = next( iter(ipr.link_lookup(ifname=action["dev"])), None) if self.idx == None: logger.warning("filter #{} references unknown interface {}".format( tc_filter["prio"], action["dev"]), extra={'iface': self.iface}) continue if "parent" in tc_filter: tc_filter["parent"] = TC.handle2int(tc_filter["parent"]) try: try: ipr.tc("replace-filter", **tc_filter) # replace seems only to work if there is no filter # => something has changed changes = True except Exception as err: if not isinstance(err, netlinkerror_classes): raise # replace does not work, supress changes result # for now #changes = True opts = { "index": self.idx, "info": tc_filter["prio"] << 16, "parent": parent, } ipr.del_filter_by_info(**opts) ipr.tc("add-filter", **tc_filter) except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning('replace filter #{} on {} failed: {}'.format( tc_filter['prio'], self.iface, err.args[1])) excpts.add('replace', err, **tc_filter) return changes
def apply(self, ignore, ign_dynamic, do_apply): logger.debug('getting addresses', extra={'iface': self.iface}) # get ifindex idx = next(iter(ipr.link_lookup(ifname=self.iface)), None) if idx == None: logger.warning('link missing', extra={'iface': self.iface}) return # get active ip addresses ipr_addr = {} addr_add = [] for addr in ipr.get_addr(index=idx): ip = ip_interface( addr.get_attr('IFA_ADDRESS') + '/' + str(addr['prefixlen'])) ipr_addr[ip] = addr for addr in self.addresses: if addr in ipr_addr: logger.info(' %s', addr.with_prefixlen, extra={ 'iface': self.iface, 'style': IfStateLogging.STYLE_OK }) del ipr_addr[addr] else: addr_add.append(addr) for ip, addr in ipr_addr.items(): if not any(ip in net for net in ignore): if not ign_dynamic or ipr_addr[ip][ 'flags'] & IFA_F_PERMANENT == IFA_F_PERMANENT: logger.info('-%s', ip.with_prefixlen, extra={ 'iface': self.iface, 'style': IfStateLogging.STYLE_DEL }) try: if do_apply: ipr.addr("del", index=idx, address=str(ip.ip), mask=ip.network.prefixlen) except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning('removing ip {}/{} failed: {}'.format( str(ip.ip), ip.network.prefixlen, err.args[1])) for addr in addr_add: logger.info('+%s', addr.with_prefixlen, extra={ 'iface': self.iface, 'style': IfStateLogging.STYLE_CHG }) if do_apply: try: ipr.addr("add", index=idx, address=str(addr.ip), mask=addr.network.prefixlen) except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning('adding ip {}/{} failed: {}'.format( str(addr.ip), addr.network.prefixlen, err.args[1]))
def apply(self, ignores, do_apply): for table, croutes in self.tables.items(): pfx = RTLookups.tables.lookup_str(table) logger.info('\nconfiguring routing table {}...'.format(pfx)) kroutes = self.kernel_routes(table) for route in croutes: if 'oif' in route and type(route['oif']) == str: route['oif'] = next( iter(ipr.link_lookup(ifname=route['oif'])), None) found = False identical = False for i, kroute in enumerate(kroutes): if route_matches(route, kroute): del kroutes[i] found = True if route_matches(route, kroute, route.keys(), indent=route['dst']): identical = True break if identical: logger.info('ok', extra={ 'iface': route['dst'], 'style': IfStateLogging.STYLE_OK }) else: if found: logger.info('change', extra={ 'iface': route['dst'], 'style': IfStateLogging.STYLE_CHG }) else: logger.info('add', extra={ 'iface': route['dst'], 'style': IfStateLogging.STYLE_CHG }) logger.debug("ip route replace: {}".format(" ".join( "{}={}".format(k, v) for k, v in route.items()))) try: if do_apply: ipr.route('replace', **route) except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning('route setup {} failed: {}'.format( route['dst'], err.args[1])) for route in kroutes: ignore = False for iroute in ignores: if route_matches(route, iroute, iroute.keys()): ignore = True break if ignore: continue logger.info('del', extra={ 'iface': route['dst'], 'style': IfStateLogging.STYLE_DEL }) try: if do_apply: ipr.route('del', **route) except Exception as err: if not isinstance(err, netlinkerror_classes): raise logger.warning('removing route {} failed: {}'.format( route['dst'], err.args[1]))