4 This script generates basic statistics about our autoinstalls.
12 from distutils.version import LooseVersion as Version
14 class NoSuchApplication(Exception):
17 class DeploymentParseError(Exception):
20 class Deployment(object):
21 def __init__(self, location, version):
22 self.location = location
23 self.version = version
26 """Parses a line from the results of parallel-find.pl.
27 This will work out of the box with fileinput"""
29 location, deploydir = line.rstrip().split(":")
31 raise DeploymentParseError
32 name = deploydir.split("/")[-1]
33 if name.find("-") != -1:
34 app, version = name.split("-")
35 elif name == "deploy":
36 # Assume that it's django, since those were botched
38 version = "0.1-scripts"
40 raise DeploymentParseError
42 return Deployment(location, applications[app].getVersion(version))
44 raise NoSuchApplication
46 """Simple method which registers the deployment as a +1 on the
47 appropriate version. No further inspection is done."""
48 self.version.count(self)
50 def count_exists(self, file):
51 """Checks if the codebase has a certain file/directory in it."""
52 if os.path.exists(self.location + "/" + file):
53 self.version.count_exists(self, file)
57 class Application(object):
59 def __init__(self, name):
62 # Some cache variables for fast access of calculated data
66 def getVersion(self, version):
67 if version not in self.versions:
68 self.versions[version] = ApplicationVersion(Version(version), self)
69 return self.versions[version]
71 return '+' * int(math.ceil(float(v)/self._max * self.HISTOGRAM_WIDTH))
73 if not self.versions: return "%-11s no installs" % self.name
75 ["%-16s %3d installs" % (self.name, self._total)] + \
76 [str(v) for v in sorted(self.versions.values())]
77 for f,c in self._c_exists.items():
78 ret.append("%d users have %s" % (c,f))
81 class ApplicationVersion(object):
82 def __init__(self, version, application):
83 self.version = version
84 self.application = application
88 return cmp(x.version, y.version)
89 def count(self, deployment):
91 self.application._total += 1
92 if self.c > self.application._max:
93 self.application._max = self.c
94 def count_exists(self, deployment, n):
95 if n in self.c_exists: self.c_exists[n] += 1
96 else: self.c_exists[n] = 1
97 if n in self.application._c_exists: self.application._c_exists[n] += 1
98 else: self.application._c_exists[n] = 1
100 return " %-12s %3d %s" \
101 % (self.version, self.c, self.application._graph(self.c))
104 "mediawiki", "wordpress", "joomla", "e107", "gallery2",
105 "phpBB", "advancedbook", "phpical", "trac", "turbogears", "django",
106 # these are technically deprecated
107 "advancedpoll", "gallery",
110 """Hash table for looking up string application name to instance"""
111 applications = dict([(n,Application(n)) for n in application_list ])
114 usage = """usage: %prog [options] [application]
116 Scans all of the collected data from parallel-find.pl, and
117 determines version histograms for our applications. You may
118 optionally pass application parameters to filter the installs."""
119 parser = optparse.OptionParser(usage)
120 parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
121 default=False, help="Print interesting directories")
122 parser.add_option("-q", "--quiet", dest="quiet", action="store_true",
123 default=False, help="Suppresses progress output")
124 parser.add_option("-d", "--version-dir", dest="version_dir",
125 default="/afs/athena.mit.edu/contrib/scripts/sec-tools/store/versions",
126 help="Location of parallel-find output")
127 parser.add_option("--count-exists", dest="count_exists",
128 default=False, help="Count deployments that contain a file")
129 # There should be machine friendly output
130 options, show_applications = parser.parse_args()
131 if not show_applications: show_applications = applications.keys()
132 show_applications = frozenset(show_applications)
133 vd = options.version_dir
135 fi = fileinput.input([vd + "/" + f for f in os.listdir(vd)])
137 print "No permissions; check if AFS is mounted"
142 hanging = False # whether or not we last outputted a newline
143 if not options.quiet: print "Processing",
146 if not options.quiet and processed % 10 == 0:
147 sys.stdout.write(".")
151 deploy = Deployment.parse(line)
152 except DeploymentParseError:
155 except NoSuchApplication:
158 if deploy.version.application.name not in show_applications: continue
160 if options.count_exists:
161 r = deploy.count_exists(options.count_exists)
162 if r and options.verbose:
166 print "Found " + options.count_exists + " in " + deploy.location
169 for app in applications.values():
170 if app.name not in show_applications: continue
173 print "With %d errors and %d unrecognized applications" % (errors, unrecognized)
175 if __name__ == "__main__":