def expand_rows(self, apply_extended=True): """Generate CIB rows by expanding all CIBs pointing to current CIB """ paths = self.resolve_graph() # for storing expanded rows rows = [] for path in paths: expanded_properties = (self.cib[uid].expand() for uid in path) for pas in itertools.product(*expanded_properties): chain = ChainMap(*pas) # For debugging purposes, add the path list to the chain. # Store as string to preserve path order (NEAT properties are not ordered). dbg_path = '<<'.join(uid for uid in path) # insert at position 0 to override any existing entries # chain.maps.insert(0, PropertyArray(NEATProperty(('cib_uids', dbg_path)))) # convert back to normal PropertyArrays row = PropertyArray(*(p for p in chain.values())) row.meta['cib_uids'] = dbg_path rows.append(row) if not apply_extended: return rows if not self.cib.extenders: # no extender CIB nodes loaded return rows # TODO optimize extended_rows = rows.copy() for entry in rows: # TODO take priorities into account # iterate extender cib_nodes for uid, xs in self.cib.extenders.items(): for pa in xs.expand(): if xs.match_entry(entry): entry_copy = copy.deepcopy(entry) chain = ChainMap(pa, entry_copy) new_pa = PropertyArray(*(p for p in chain.values())) try: del new_pa['uid'] except KeyError: pass extended_rows.append(new_pa) return extended_rows
def lookup(self, input_properties, candidate_num=5): """CIB lookup logic implementation Return CIB rows that include *all* required properties from the request PropertyArray """ assert isinstance(input_properties, PropertyArray) candidates = [input_properties] for e in self.rows: try: # FIXME better check whether all input properties are included in row - improve matching # ignore optional properties in input request required_pa = PropertyArray( *(p for p in input_properties.values() if p.precedence == NEATProperty.IMMUTABLE)) if len(required_pa & e) != len(required_pa): continue except ImmutablePropertyError: continue try: candidate = e + input_properties candidate.cib_node = e.cib_node candidates.append(candidate) except ImmutablePropertyError: pass return sorted(candidates, key=operator.attrgetter('score'), reverse=True)[:candidate_num]
def __init__(self, node_dict=None): if node_dict is None: node_dict = dict() if not isinstance(node_dict, dict): raise CIBEntryError("invalid CIB object") self.root = node_dict.get('root', False) # otherwise chain matched CIBs self.link = node_dict.get('link', False) self.priority = node_dict.get('priority', 0) # TTL for the CIB node: the node is considered invalid after the time specified self.expire = node_dict.get('expire', None) or node_dict.get('expires', None) # FIXME expires is deprecated self.filename = node_dict.get('filename', None) self.description = node_dict.get('description', '') # convert to PropertyMultiArray with NEATProperties properties = node_dict.get('properties', []) if not isinstance(properties, list): # properties should be in a list. The list elements are expanded when generating the CIB rows. properties = [properties] self.properties = PropertyMultiArray() for p in properties: if isinstance(p, list): self.properties.add([PropertyArray.from_dict(ps) for ps in p]) else: self.properties.add(PropertyArray.from_dict(p)) self.match = [] # FIXME better error handling if match undefined for l in node_dict.get('match', []): # convert to NEATProperties self.match.append(PropertyArray.from_dict(l)) self.linked = set() if self.link and not self.match: logging.warning('link attribute set but no match field!') self.uid = node_dict.get('uid') if self.uid is None: self.uid = self._gen_uid()
def __init__(self, policy_dict=None, uid=None): # set default values if policy_dict is None: policy_dict = dict() if uid is not None: policy_dict['uid'] = uid # TODO do we need to handle unknown attributes? for k, v in policy_dict.items(): if isinstance(v, str): setattr(self, k, v) self.priority = int(policy_dict.get('priority', 0)) self.replace_matched = policy_dict.get('replace_matched', False) self.filename = None self.time = time.time() # parse match fields match = policy_dict.get('match', {}) self.match = PropertyArray() self.match.add(*dict_to_properties(match)) # parse augment properties properties = policy_dict.get('properties', []) if not isinstance(properties, list): # properties should be in a list. properties = [properties] self.properties = PropertyMultiArray() for p in properties: if isinstance(p, list): self.properties.add([PropertyArray.from_dict(ps) for ps in p]) else: self.properties.add(PropertyArray.from_dict(p)) # set UID self.uid = policy_dict.get('uid') if self.uid is None: self.uid = self.__gen_uid() else: self.uid = str(self.uid).lower()
def gen_cibs(): for en in netifaces.interfaces(): c = cib.CIBNode() c.uid = en c.description = "autogenerated CIB node for local interface %s" % en c.root = True c.filename = en + '.cib' c.expire = -1 pa = PropertyArray() pa.add( NEATProperty(('interface', en), precedence=NEATProperty.IMMUTABLE)) pa.add( NEATProperty(('local_interface', True), precedence=NEATProperty.IMMUTABLE)) c.properties.add(pa) addr_list = [] for af, addresses in netifaces.ifaddresses(en).items(): if af == netifaces.AF_INET: af_prop = NEATProperty(('ip_version', 4), precedence=NEATProperty.IMMUTABLE) elif af == netifaces.AF_INET6: af_prop = NEATProperty(('ip_version', 6), precedence=NEATProperty.IMMUTABLE) else: continue for addr in addresses: pa = PropertyArray() pa.add( NEATProperty(('local_ip', addr.get('addr', 0)), precedence=NEATProperty.IMMUTABLE)) pa.add(af_prop) addr_list.append(pa) if addr_list: c.properties.add(addr_list) yield c.json()
print(term_separator("CIB END")) def __repr__(self): return 'CIB<%d>' % (len(self.nodes)) if __name__ == "__main__": cib = CIB('./cib/example/') b = cib['B'] c = cib['C'] cib.dump() import code code.interact(local=locals(), banner='CIB') for uid in cib.roots: z = cib[uid].resolve_links([]) print(z) query = PropertyArray() test_request_str = '{"MTU": {"value": [1500, Infinity]}, "low_latency": {"precedence": 2, "value": true}, "remote_ip": {"precedence": 2, "value": "10:54:1.23"}, "transport": {"value": "TCP"}}' test = json.loads(test_request_str) for k, v in test.items(): query.add(NEATProperty((k, v['value']), precedence=v.get('precedence', 1))) candidates = cib.lookup(query) for i in candidates: print(i) # print(i, i.cib_node, i.score)
class NEATPolicy(object): """NEAT policy representation""" def __init__(self, policy_dict=None, uid=None): # set default values if policy_dict is None: policy_dict = dict() if uid is not None: policy_dict['uid'] = uid # TODO do we need to handle unknown attributes? for k, v in policy_dict.items(): if isinstance(v, str): setattr(self, k, v) self.priority = int(policy_dict.get('priority', 0)) self.replace_matched = policy_dict.get('replace_matched', False) self.filename = None self.time = time.time() # parse match fields match = policy_dict.get('match', {}) self.match = PropertyArray() self.match.add(*dict_to_properties(match)) # parse augment properties properties = policy_dict.get('properties', []) if not isinstance(properties, list): # properties should be in a list. properties = [properties] self.properties = PropertyMultiArray() for p in properties: if isinstance(p, list): self.properties.add([PropertyArray.from_dict(ps) for ps in p]) else: self.properties.add(PropertyArray.from_dict(p)) # set UID self.uid = policy_dict.get('uid') if self.uid is None: self.uid = self.__gen_uid() else: self.uid = str(self.uid).lower() def __gen_uid(self): # TODO make UID immutable? s = str(id(self)) return hashlib.md5(s.encode('utf-8')).hexdigest() def dict(self): d = {} for attr in ['uid', 'priority', 'replace_matched', 'filename', 'time']: try: d[attr] = getattr(self, attr) except AttributeError: logging.warning("Policy doesn't contain attribute %s" % attr) d['match'] = self.match.dict() d['properties'] = self.properties.list() return d def json(self): return json.dumps(self.dict(), indent=4, sort_keys=True) def match_len(self): """Use the number of match elements to sort the entries in the PIB. Entries with the smallest number of elements are matched first.""" return len(self.match) def match_query(self, input_properties, strict=False): """Check if the match properties are completely covered by the properties of a query. If strict flag is set match only properties with precedences that are higher or equal to the precedence of the corresponding match property. """ # always return True if the match field is empty (wildcard) if not self.match: return True # TODO check # find full overlap? if not self.match.items() <= input_properties.items(): return # find intersection matching_props = self.match.items() & input_properties.items() if strict: # ignore properties with a lower precedence than the associated match property return bool({k for k, v in matching_props if input_properties[k].precedence >= self.match[k].precedence}) else: return bool(matching_props) def apply(self, properties: PropertyArray): """Apply policy properties to a set of candidate properties.""" for p in self.properties.values(): logging.info("applying property %s" % p) properties.add(*p) def __str__(self): return '%3s. %-8s %s %s %s' % (self.priority, self.uid, self.match, PM.CHARS.RIGHT_ARROW, self.properties) def __repr__(self): return repr({a: getattr(self, a) for a in ['uid', 'match', 'properties', 'priority']})
def apply(self, properties: PropertyArray): """Apply policy properties to a set of candidate properties.""" for p in self.properties.values(): logging.info("applying property %s" % p) properties.add(*p)
class NEATPolicy(object): """NEAT policy representation""" def __init__(self, policy_dict=None, uid=None): # set default values if policy_dict is None: policy_dict = dict() if uid is not None: policy_dict['uid'] = uid # TODO do we need to handle unknown attributes? for k, v in policy_dict.items(): if isinstance(v, str): setattr(self, k, v) self.priority = int(policy_dict.get('priority', 0)) self.replace_matched = policy_dict.get('replace_matched', False) self.filename = None self.time = time.time() # parse match fields match = policy_dict.get('match', {}) self.match = PropertyArray() self.match.add(*dict_to_properties(match)) # parse augment properties properties = policy_dict.get('properties', []) if not isinstance(properties, list): # properties should be in a list. properties = [properties] self.properties = PropertyMultiArray() for p in properties: if isinstance(p, list): self.properties.add([PropertyArray.from_dict(ps) for ps in p]) else: self.properties.add(PropertyArray.from_dict(p)) # set UID self.uid = policy_dict.get('uid') if self.uid is None: self.uid = self.__gen_uid() else: self.uid = str(self.uid).lower() def __gen_uid(self): # TODO make UID immutable? s = str(id(self)) return hashlib.md5(s.encode('utf-8')).hexdigest() def dict(self): d = {} for attr in ['uid', 'priority', 'replace_matched', 'filename', 'time']: try: d[attr] = getattr(self, attr) except AttributeError: logging.warning("Policy doesn't contain attribute %s" % attr) d['match'] = self.match.dict() d['properties'] = self.properties.list() return d def json(self): return json.dumps(self.dict(), indent=4, sort_keys=True) def match_len(self): """Use the number of match elements to sort the entries in the PIB. Entries with the smallest number of elements are matched first.""" return len(self.match) def match_query(self, input_properties, strict=False): """Check if the match properties are completely covered by the properties of a query. If strict flag is set match only properties with precedences that are higher or equal to the precedence of the corresponding match property. """ # always return True if the match field is empty (wildcard) if not self.match: return True # TODO check # find full overlap? if not self.match.items() <= input_properties.items(): return # find intersection matching_props = self.match.items() & input_properties.items() if strict: # ignore properties with a lower precedence than the associated match property return bool({ k for k, v in matching_props if input_properties[k].precedence >= self.match[k].precedence }) else: return bool(matching_props) def apply(self, properties: PropertyArray): """Apply policy properties to a set of candidate properties.""" for p in self.properties.values(): logging.info("applying property %s" % p) properties.add(*p) def __str__(self): return '%3s. %-8s %s %s %s' % (self.priority, self.uid, self.match, PM.CHARS.RIGHT_ARROW, self.properties) def __repr__(self): return repr({ a: getattr(self, a) for a in ['uid', 'match', 'properties', 'priority'] })