import logging import validator.bondethernet as bondethernet class NullHandler(logging.Handler): def emit(self, record): pass def get_parent_by_name(yaml, ifname): """ Returns the sub-interface's parent, or None if the sub-int doesn't exist. """ if not '.' in ifname: return None ifname, subid = ifname.split('.') subid = int(subid) try: iface = yaml['interfaces'][ifname] return iface except: pass return None def get_by_name(yaml, ifname): """ Returns the interface or sub-interface by a given name, or None if it does not exist """ if '.' in ifname: ifname, subid = ifname.split('.') subid = int(subid) try: iface = yaml['interfaces'][ifname]['sub-interfaces'][subid] return iface except: return None try: iface = yaml['interfaces'][ifname] return iface except: pass return None def has_sub(yaml, ifname): """ Returns True if this interface has sub-interfaces """ if not 'interfaces' in yaml: return False if ifname in yaml['interfaces']: iface = yaml['interfaces'][ifname] if 'sub-interfaces' in iface and len(iface['sub-interfaces']) > 0: return True return False def has_address(yaml, ifname): """ Returns True if this interface or sub-interface has one or more addresses""" if not 'interfaces' in yaml: return False if '.' in ifname: ifname, subid = ifname.split('.') subid = int(subid) try: if len(yaml['interfaces'][ifname]['sub-interfaces'][subid]['addresses']) > 0: return True except: pass return False try: if len(yaml['interfaces'][ifname]['addresses']) > 0: return True except: pass return False def is_bond_member(yaml, ifname): """ Returns True if this interface is a member of a BondEthernet """ if not 'bondethernets' in yaml: return False for bond, iface in yaml['bondethernets'].items(): if not 'interfaces' in iface: continue if ifname in iface['interfaces']: return True return False def unique_lcp(yaml, ifname): """ Returns true if this interface has a unique LCP name """ if not 'interfaces' in yaml: return True iface = get_by_name(yaml, ifname) lcp = get_lcp(yaml, ifname) if not lcp: return True ncount = 0 for sibling_ifname, sibling_iface in yaml['interfaces'].items(): sibling_lcp = get_lcp(yaml, sibling_ifname) if sibling_lcp == lcp and sibling_ifname != ifname: ## print("%s overlaps with %s: %s" % (ifname, sibling_ifname, lcp)) ncount = ncount + 1 if ncount == 0: return True return False def has_lcp(yaml, ifname): """ Returns True if this interface or sub-interface has an LCP""" if not 'interfaces' in yaml: return False if '.' in ifname: ifname, subid = ifname.split('.') subid = int(subid) try: if len(yaml['interfaces'][ifname]['sub-interfaces'][subid]['lcp']) > 0: return True except: pass return False try: if len(yaml['interfaces'][ifname]['lcp']) > 0: return True except: pass return False def unique_encapsulation(yaml, sub_ifname): """ Ensures that for the sub_ifname specified, there exist no other sub-ints on the parent with the same encapsulation. """ iface = get_by_name(yaml, sub_ifname) parent_iface = get_parent_by_name(yaml, sub_ifname) if not iface or not parent_iface: return False parent_ifname, subid = sub_ifname.split('.') dot1q = 0 dot1ad = 0 inner_dot1q = 0 if not 'encapsulation' in iface: dot1q = int(subid) else: if 'dot1q' in iface['encapsulation']: dot1q = iface['encapsulation']['dot1q'] elif 'dot1ad' in iface['encapsulation']: dot1ad = iface['encapsulation']['dot1ad'] if 'inner-dot1q' in iface['encapsulation']: inner_dot1q = iface['encapsulation']['inner-dot1q'] ncount = 0 for subid, sibling_iface in parent_iface['sub-interfaces'].items(): sibling_dot1q = 0 sibling_dot1ad = 0 sibling_inner_dot1q = 0 sibling_ifname = "%s.%d" % (parent_ifname, subid) if not 'encapsulation' in sibling_iface: sibling_dot1q = subid else: if 'dot1q' in sibling_iface['encapsulation']: sibling_dot1q = sibling_iface['encapsulation']['dot1q'] elif 'dot1ad' in sibling_iface['encapsulation']: sibling_dot1ad = sibling_iface['encapsulation']['dot1ad'] if 'inner-dot1q' in sibling_iface['encapsulation']: sibling_inner_dot1q = sibling_iface['encapsulation']['inner-dot1q'] if (dot1q,dot1ad,inner_dot1q) == (sibling_dot1q, sibling_dot1ad, sibling_inner_dot1q) and sub_ifname != sibling_ifname: ## print("%s overlaps with %s: [%d,%d,%d]" % (sub_ifname, sibling_ifname, dot1q, dot1ad, inner_dot1q)) ncount = ncount + 1 if (ncount == 0): return True return False def valid_encapsulation(yaml, sub_ifname): try: ifname, subid = sub_ifname.split('.') subid = int(subid) sub_iface = yaml['interfaces'][ifname]['sub-interfaces'][subid] except: return False if not 'encapsulation' in sub_iface: return True encap = sub_iface['encapsulation'] if 'dot1ad' in encap and 'dot1q' in encap: return False if 'inner-dot1q' in encap and not ('dot1ad' in encap or 'dot1q' in encap): return False if 'exact-match' in encap and encap['exact-match'] == False and is_l3(yaml, sub_ifname): return False return True def is_l3(yaml, ifname): """ Returns True if the interface exists and has either an LCP or an address """ iface = get_by_name(yaml, ifname) if not iface: return False if has_lcp(yaml, ifname): return True if has_address(yaml, ifname): return True return False def get_lcp(yaml, ifname): """ Returns the LCP of the interface. If the interface is a sub-interface with L3 enabled, synthesize it based on its parent, using smart QinQ syntax. Return None if no LCP can be found. """ iface = get_by_name(yaml, ifname) parent_iface = get_parent_by_name(yaml, ifname) if 'lcp' in iface: return iface['lcp'] if not is_l3(yaml, ifname): return None if not 'lcp' in parent_iface: return None if not 'encapsulation' in iface: if not '.' in ifname: ## Not a sub-int and no encap? Should not happen return None ifname, subid = ifname.split('.') subid = int(subid) return "%s.%d" % (parent_iface['lcp'], subid) dot1q = 0 dot1ad = 0 inner_dot1q = 0 if 'dot1q' in iface['encapsulation']: dot1q = iface['encapsulation']['dot1q'] elif 'dot1ad' in iface['encapsulation']: dot1ad = iface['encapsulation']['dot1ad'] if 'inner-dot1q' in iface['encapsulation']: inner_dot1q = iface['encapsulation']['inner-dot1q'] if inner_dot1q and dot1ad: lcp = "%s.%d.%d" % (parent_iface['lcp'], dot1ad, inner_dot1q) elif inner_dot1q and dot1q: lcp = "%s.%d.%d" % (parent_iface['lcp'], dot1q, inner_dot1q) elif dot1ad: lcp = "%s.%d" % (parent_iface['lcp'], dot1ad) elif dot1q: lcp = "%s.%d" % (parent_iface['lcp'], dot1q) else: return None return lcp def get_mtu(yaml, ifname): """ Returns MTU of the interface. If it's not set, return the parent's MTU, and return 1500 if no MTU was set on the sub-int or the parent.""" iface = get_by_name(yaml, ifname) parent_iface = get_parent_by_name(yaml, ifname) try: return iface['mtu'] return parent_iface['mtu'] except: pass return 1500 def validate_interfaces(yaml): result = True msgs = [] logger = logging.getLogger('vppcfg.validator') logger.addHandler(NullHandler()) if not 'interfaces' in yaml: return result, msgs for ifname, iface in yaml['interfaces'].items(): logger.debug("interface %s" % iface) if ifname.startswith("BondEthernet") and not bondethernet.get_by_name(yaml, ifname): msgs.append("interface %s does not exist in bondethernets" % ifname) result = False iface_mtu = get_mtu(yaml, iface) iface_lcp = has_lcp(yaml, ifname) iface_address = has_address(yaml, ifname) if iface_address and not iface_lcp: msgs.append("interface %s has adddress(es) but no LCP" % ifname) result = False if iface_lcp and not unique_lcp(yaml, ifname): lcp = get_lcp(yaml, ifname) msgs.append("interface %s does not have a unique LCP name %s" % (ifname, lcp)) result = False if has_sub(yaml, ifname): for sub_id, sub_iface in yaml['interfaces'][ifname]['sub-interfaces'].items(): logger.debug("sub-interface %s" % sub_iface) sub_ifname = "%s.%d" % (ifname, sub_id) if not sub_iface: msgs.append("sub-interface %s has no config" % (sub_ifname)) result = False continue sub_lcp = get_lcp(yaml, sub_ifname) if sub_lcp and len(sub_lcp)>15: msgs.append("sub-interface %s has LCP with too long name '%s'" % (sub_ifname, sub_lcp)) result = False sub_mtu = get_mtu(yaml, sub_ifname) if sub_mtu > iface_mtu: msgs.append("sub-interface %s has MTU %d higher than parent MTU %d" % (sub_ifname, sub_iface['mtu'], iface_mtu)) result = False if has_lcp(yaml, sub_ifname): if not iface_lcp: msgs.append("sub-interface %s has LCP but %s does not have LCP" % (sub_ifname, ifname)) result = False if has_address(yaml, sub_ifname): ## The sub_iface lcp is not required: it can be derived from the iface_lcp, which has to be set if not iface_lcp: msgs.append("sub-interface %s has address(es) but %s does not have LCP" % (sub_ifname, ifname)) result = False if not valid_encapsulation(yaml, sub_ifname): msgs.append("sub-interface %s has invalid encapsulation" % (sub_ifname)) result = False elif not unique_encapsulation(yaml, sub_ifname): msgs.append("sub-interface %s doesn't have unique encapsulation" % (sub_ifname)) result = False return result, msgs