def _load_unit(self, u): verb(2, "Loading data for unit " + str(u)) name = u.name #print("Load "+name) if name not in self.deferred: #print("Already loaded") return u lazy = set() for kind in self.processing: flip = kind in [Unit.AFTER, Unit.REQUIRES] #ep("Processing relationship "+kind) other_names = self.backend.unit_property(name, kind) or [] for other in other_names: if not other: continue if kind == Unit.MOUNTS: other = 'GEN-' + other.lstrip('/').replace('/', '-') + '.mount' #ep("Mapping RequiresMountsFor to new mount unit " + other) ou = self._unit(other) lazy.add(ou) u.add_relationship(kind, ou) u.loaded = True self.deferred.remove(name) for z in lazy: self._load_unit(z) return u
def vf(data, iteration, origin, steps): if data.nid in record.visited: verb("This node was already visited.") return False verb("This node not visited yet.") record.visited.append(data.nid) record.origins[data.nid] = origin return True
def unit_property(self, name, kind): escaped_name = [ '--', name ] if name.startswith('-') else [ name ] verb(2, "Escaped name for '{}' is {}".format(str(name), pformat(escaped_name))) result = self._capture(LiveBackend.SHOW_PROPERTY + [ kind ] + escaped_name) if result: other_names = result[0].strip().split(' ') if len(other_names) > 1 or other_names[0] != '': return other_names return []
def test_visit_node(): v, g, record = new_visitor(data={'a-c': 'ac', 'b-a': 'ba'}, sorting=True) a = g.node('a') b = g.node('b') c = g.node('c') assert None not in [a, b, c] v.Visit() verb(pformat(record)) assert record.origins['c'] == a assert record.origins['b'] is None
def Visit(self): self.init_data() # NOTE the set of nodes to visit is used like this # because it can grow during the visiting, if new nodes are discovered. iteration = 0 while len(self.to_visit): nid = self.to_visit.pop() data = self.datas[nid] iteration += 1 verb("Visiting {} with iteration = {} step 0 from nowhere".format( data.nid, iteration)) self.visit_node(data, iteration, None, 0) return self.datas
def search(self, kinds, prune=None): for u in self.Units(): u = self.Unit(u.name) all_set = False statuses = set() name_matchers = [] for k in kinds: if k == 'all': all_set = True elif k in Unit.STATUS: statuses.add(k) else: ep("Adding name matcher " + str(k)) name_matchers.append(k) startwith = set() if all_set: verb("All units selected as a starting point") for u in self.Units(): startwith.add(u.name) else: ep("Searching " + str(len(self.Units())) + " with " + str(len(name_matchers)) + " name matchers") for u in self.Units(): for nm in name_matchers: if re.search(nm, u.name): ep("matched " + u.name) startwith.add(u.name) break elif u.name.startswith("test-loop"): ep("matcher " + str(nm) + " did not match " + u.name) #else: # ep("non test-loop") verb2("Starting with a set of units:" + pformat(startwith)) if statuses: filterout = set() verb("Excluding units that do not match statuses " + pformat(statuses)) for u in self.Units(): if not u.status_match(statuses): filterout.add(u.name) startwith = startwith - filterout if prune: filterout = set() verb("Excluding units that do not match names " + pformat(prune)) for u in self.Units(): for nm in prune: if re.search(nm, u.name): filterout.add(u.name) break startwith = startwith - filterout return [self.Unit(x) for x in startwith]
def visit_node(self, data, iteration, origin, steps): nid = data.nid n = data.n # start with this node and visit it #verb(f"here, nid={nid}, n={n}, iter={iteration}, origin={origin}") visit_further = self.visit_func(data, iteration, origin, steps) # Note that iteration and origin are set *after* the visit function, # however the visit function receives those in the function call, # it is not set yet in the object so that the visitor an check if there # is a pre-existing value. However it is set prior to moving on to the # next node, so that the historical value is maintained data.i = iteration data.o = origin visit_also = [] if not visit_further: verb("Will not visit further from node " + str(nid)) if visit_further: #verb(f"Node {nid} - checking edges.") for e in n.Edges(): verb("Edge {}".format(e)) if e.b.id == nid: if e.a.id == nid: log.warning("self-loop") else: log.warning("Back-to-front edge!") visit_also.append(e.a.id()) else: visit_also.append(e.b.id()) if not visit_also: verb("{} is terminal or visitor returned False.".format(nid)) else: verb("Will visit {} nodes from here".format(len(visit_also))) if self.sorting: visit_also = sorted(visit_also) verb(" Will visit also " + ", ".join(visit_also)) for vid in visit_also: if vid is not None: vdata = data.d.get(vid, None) if not vdata: verb( "****BUG***** visitor data for {} not yet initialised - edge to un-indexed node, Creating it." .format(vid)) vdata = self.init_node(nid=vid) data.v = vdata self.visit_node(vdata, iteration, n, steps + 1) data.v = None
def Graph(self, name, backwards=True, units=None): if name is None: name = "systemd" g = Graph(name) ep("Generating graph for " + str(len(units)) + " selected units") # First preprocess the units to pre-load and generate extras if required if units is None: units = self.Units() todo = set() done = set() for u in units: u = self.Unit(u.name) todo.add(u.name) # It is simple to go forwards, just follow the edges if not backwards: while len(todo): #ep("TODO: "+str(len(todo))) uname = todo.pop() if uname in done: #ep("Aldready done") continue u = self.Unit(uname) #ep("Add node " + u.name + " with " + str(len(u.Edges())) + " edges") g.add_node(u) done.add(uname) for e in u.Edges(): #ep("Check edge " + str(e)) a = e.a b = e.b if e.a.name not in done: todo.add(e.a.name) if e.b.name not in done: todo.add(e.b.name) #ep("Add edge " + str(e)) g.add_edge(e) else: #ep("Search network for forward links to these nodes") # we are going backwards, need to search the entire set of nodes # for forward edges that point to these nodes. while len(todo): verb("TODO: " + str(len(todo))) uname = todo.pop() if uname in done: continue u = self.Unit(uname) g.add_node(u) done.add(uname) for u2 in self.Units(): verb("Search for units with edges going to " + uname) if u2.name == uname: continue elif u2.name in done: continue elif u2.name in todo: # do it later continue else: for e in u2.Edges(): b = e.b if b.name == uname: aname = e.a.name if aname not in todo and aname not in done: g.add_node(self.Unit(aname)) g.add_edge(e) #ep("Add edge " + str(e)) todo.add(aname) # Now we will have all interesting nodes in 'done' return g