script to ~build out a list of lists
authorAlex Dehnert <adehnert@mit.edu>
Mon, 12 Mar 2012 18:28:52 +0000 (14:28 -0400)
committerAlex Dehnert <adehnert@mit.edu>
Mon, 12 Mar 2012 18:28:52 +0000 (14:28 -0400)
In particular, given a starting list, this computes the transitive closure
under the operations:
* get the lists this list owns
* get the lists this list contains
* (disabled) get the lists that contain this list

It allows specifying a list whose members should be skipped, as well.

The code is easy to configure for which of those operations to use, and
whether to do it recursively or not. At the moment, this isn't exposed to
the command line, though that would be a reasonable feature to add.

compute-list-closure [new file with mode: 0755]

diff --git a/compute-list-closure b/compute-list-closure
new file mode 100755 (executable)
index 0000000..cfbc771
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+
+import moira
+import sys
+import Queue
+
+def lenient_query(*query):
+    try:
+        results = moira.query(*query)
+    except moira.MoiraException as e:
+        msg = e[1]
+        if msg == 'No records in database match query':
+            results = []
+        else:
+            raise
+    return results
+    
+
+def expand_closure(lst, owns=True, members=True, member_of=True, recursive=True, ):
+    closure = set()
+    include_type = 'list'
+    if recursive: include_type = 'rlist'
+    if owns:
+        results = lenient_query('get_ace_use', include_type, lst)
+        for result in results:
+            if result['use_type'] == 'LIST':
+                closure.add(result['use_name'])
+
+    if members:
+        results = lenient_query('get_members_of_list', lst)
+        for result in results:
+            if result['member_type'] == 'LIST':
+                closure.add(result['member_name'])
+
+    if member_of:
+        results = lenient_query('get_lists_of_member', include_type, lst)
+        for result in results:
+            closure.add(result['list_name'])
+
+    return closure
+
+def get_exclusions(lst):
+    exclusions = set()
+    results = lenient_query('get_members_of_list', lst)
+    for result in results:
+        if result['member_type'] == 'LIST':
+            exclusions.add(result['member_name'])
+    return exclusions
+
+def compute_closure(lists, exclude):
+    closure = set()
+    parent = {}
+
+    the_queue = Queue.Queue()
+    for lst in lists:
+        the_queue.put(lst)
+    for lst in lists:
+        parent[lst] = None
+
+    while not the_queue.empty():
+        lst = the_queue.get()
+        if lst in closure:
+            print "skipping %s" % (lst, )
+            continue
+        closure.add(lst)
+        try:
+            new_lists = expand_closure(lst, member_of=False, recursive=False, )
+        except moira.MoiraException as e:
+            msg = e[1]
+            if msg == 'Insufficient permission to perform requested database access':
+                new_lists = []
+                print >>sys.stderr, "List %s (parent %s) appears to be hidden; ignoring. (Queue length: %d; closure length: %d)" % (lst, parent[lst], the_queue.qsize(), len(closure), )
+            else:
+                raise
+        for new_list in new_lists:
+            if new_list in closure:
+                pass
+            elif new_list in exclude:
+                print "Excluding %s (parent %s)" % (new_list, lst)
+            else:
+                the_queue.put(new_list)
+                parent[new_list] = lst
+                print "%s (from %s)" % (new_list, lst)
+    return closure
+
+if __name__ == '__main__':
+    moira.connect()
+    moira.auth('awdflu') # client name is awdflu --- Alex Dehnert file-lists-updater
+    starter_list = sys.argv[1]
+    exclude_list = sys.argv[2]
+    exclude = get_exclusions(exclude_list)
+    closure = compute_closure([starter_list], exclude)
+    moira.disconnect()
+    print "\n\n\n"
+    for lst in closure:
+        print lst
+    print
+    print "Count:", len(closure)