__docformat__ = 'restructuredtext' from category import Category from pattern import Pattern import setcross class StateMachine: """ The state machine contains a list of dependencies between states, and a list of states which are "up." """ def __init__(self): """ Create a new state machine """ self.up = set() self.deps = [] def bring_up(self, cat): """ Move states in the given Category `cat` from down to up. """ found = None for (match, dependency) in self.get_applicable_deps(cat): res = self.get_satisfied_states(match, dependency) if len(res) == 0: return False if found == None: found = [res] else: found.append(res) if found == None: self.add_hold(cat) return True to_add = self.cat_cross(found) for x in to_add: self.add_hold(x) return True def cat_cross(self, found): """ Given a list of sets, where each set contains Category objects, return a set of all categories that can be made by intersecting one element from each set. """ to_add = set() for tup in setcross.cross(*found): orig = tup while len(tup) > 1: newtup = (tup[0].intersect(tup[1]),) if newtup[0] == None: tup = () break tup = newtup + tup[2:len(tup)] if len(tup) == 0 or tup[0] == None: continue to_add.add(tup[0]) return to_add def intersect_list(self, cats1, cats2): """ Given two lists of categories, return a list of categories such that a state appearing in at least one category in each list will appear in at least one category in the returned list. """ retval = set() for x in cats1: for y in cats2: inter = x.intersect(y) if inter != None: retval.add(inter) return retval def add_hold(self, cat): """ Add a hold to a state. Does not check dependencies. """ for x in self.up: if cat.subset_of(x): return self.up.add(cat) def get_satisfied_states(self, dependents, dependencies): """ Given that states in `dependents` depend on states in `dependencies`, return a new Category that contains only the states in `dependents` that could match states in `dependencies`. """ retval = [] for cat in self.up: if dependencies.equiv(cat): retval.append(dependents.fill(cat.args)) return set(retval) | dependents.inverse_set() def get_applicable_deps(self, cat): """ Find dependencies that might apply to members of `cat` """ retval = [] for (x, y) in self.deps: if x.equiv(cat): un = cat.intersect(x) retval.append((un, y.fill(un.args))) return retval def __str__(self): return "\n".join(["%s" % k for k in self.up]) def __repr__(self): return str(self) if __name__ == "__main__": sm = StateMachine() sm.deps.append((Category("mounted", type=Pattern(True, "nfs")), Category("network_up"))) sm.deps.append((Category("mounted", uuid=Pattern(False), devname=Pattern(False), label=Pattern(False), type=Pattern(False, "nfs")), Category("found_disk", uuid=Pattern(False), devname=Pattern(False), label=Pattern(False)))) sm.deps.append((Category("mounted", uuid=Pattern(False), devname=Pattern(False), label=Pattern(False)), Category("vol_conf", uuid=Pattern(False), devname=Pattern(False), label=Pattern(False)))) sm.bring_up(Category("network_up")) sm.bring_up(Category("vol_conf", uuid=Pattern(False), devname=Pattern(False), label=Pattern(True, "myroot"), type=Pattern(True, "ext3"), mountpoint=Pattern(True, "/"))) sm.bring_up(Category("vol_conf", uuid=Pattern(False), devname=Pattern(True, "foosrv.com:/vol/home"), label=Pattern(False), type=Pattern(True, "nfs"), mountpoint=Pattern(True, "/home"))) sm.bring_up(Category("found_disk", uuid=Pattern(True, "d3adb3ef"), devname=Pattern(True, "/dev/sda"), label=Pattern(True, "myroot"))) sm.bring_up(Category("mounted", uuid=Pattern(False), type=Pattern(False), devname=Pattern(False), label=Pattern(False), mountpoint=Pattern(False))) print sm