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
24 self.application = version.application
27 """Parses a line from the results of parallel-find.pl.
28 This will work out of the box with fileinput"""
30 location, deploydir = line.rstrip().split(":")
32 raise DeploymentParseError
33 name = deploydir.split("/")[-1]
34 if name.find("-") != -1:
35 app, version = name.split("-")
36 elif name == "deploy":
37 # Assume that it's django, since those were botched
39 version = "0.1-scripts"
41 raise DeploymentParseError
43 return Deployment(location, applications[app].getVersion(version))
45 raise NoSuchApplication
47 """Simple method which registers the deployment as a +1 on the
48 appropriate version. No further inspection is done."""
49 self.version.count(self)
51 def count_exists(self, file):
52 """Checks if the codebase has a certain file/directory in it."""
53 if os.path.exists(self.location + "/" + file):
54 self.version.count_exists(self, file)
58 class Application(object):
60 def __init__(self, name):
63 # Some cache variables for fast access of calculated data
67 def getVersion(self, version):
68 if version not in self.versions:
69 self.versions[version] = ApplicationVersion(Version(version), self)
70 return self.versions[version]
72 return '+' * int(math.ceil(float(v)/self._max * self.HISTOGRAM_WIDTH))
74 if not self.versions: return "%-11s no installs" % self.name
76 ["%-16s %3d installs" % (self.name, self._total)] + \
77 [str(v) for v in sorted(self.versions.values())]
78 for f,c in self._c_exists.items():
79 ret.append("%d users have %s" % (c,f))
82 class ApplicationVersion(object):
83 def __init__(self, version, application):
84 self.version = version
85 self.application = application
89 return cmp(x.version, y.version)
90 def count(self, deployment):
92 self.application._total += 1
93 if self.c > self.application._max:
94 self.application._max = self.c
95 def count_exists(self, deployment, n):
96 if n in self.c_exists: self.c_exists[n] += 1
97 else: self.c_exists[n] = 1
98 if n in self.application._c_exists: self.application._c_exists[n] += 1
99 else: self.application._c_exists[n] = 1
101 return " %-12s %3d %s" \
102 % (self.version, self.c, self.application._graph(self.c))
105 "mediawiki", "wordpress", "joomla", "e107", "gallery2",
106 "phpBB", "advancedbook", "phpical", "trac", "turbogears", "django",
107 # these are technically deprecated
108 "advancedpoll", "gallery",
111 """Hash table for looking up string application name to instance"""
112 applications = dict([(n,Application(n)) for n in application_list ])
115 usage = """usage: %prog [options] [application]
117 Scans all of the collected data from parallel-find.pl, and
118 determines version histograms for our applications. You may
119 optionally pass application parameters to filter the installs."""
120 parser = optparse.OptionParser(usage)
121 parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
122 default=False, help="Print interesting directories")
123 parser.add_option("-q", "--quiet", dest="quiet", action="store_true",
124 default=False, help="Suppresses progress output")
125 parser.add_option("-d", "--version-dir", dest="version_dir",
126 default="/afs/athena.mit.edu/contrib/scripts/sec-tools/store/versions",
127 help="Location of parallel-find output")
128 parser.add_option("--count-exists", dest="count_exists",
129 default=False, help="Count deployments that contain a file")
130 # There should be machine friendly output
131 options, show = parser.parse_args()
132 if not show: show = applications.keys()
133 show = frozenset(show)
134 vd = options.version_dir
136 fi = fileinput.input([vd + "/" + f for f in os.listdir(vd)])
138 print "No permissions; check if AFS is mounted"
143 # I really don't like this boolean
144 hanging = False # whether or not we last outputted a newline
145 if not options.quiet: print "Processing",
148 if not options.quiet and processed % 10 == 0:
149 sys.stdout.write(".")
153 deploy = Deployment.parse(line)
154 except DeploymentParseError:
157 except NoSuchApplication:
160 if deploy.application.name + "-" + str(deploy.version.version) in show:
164 print "%s-%s deployment at %s" \
165 % (deploy.application.name, deploy.version.version, deploy.location)
166 elif deploy.application.name in show:
171 if options.count_exists:
172 r = deploy.count_exists(options.count_exists)
173 if r and options.verbose:
177 print "Found " + options.count_exists + " in " + deploy.location
180 for app in applications.values():
181 if app.name not in show: continue
184 print "With %d errors and %d unrecognized applications" % (errors, unrecognized)
186 if __name__ == "__main__":