3539cabb70325e8acac5f0954b49f90b51dbfe8e
[myplc.git] / support-scripts / gen-static-content.py
1 #!/usr/bin/env /usr/bin/plcsh
2 #
3 # Generates static versions of expensive web pages
4 #
5 # Mark Huang <mlhuang@cs.princeton.edu>
6 # Copyright (C) 2005 The Trustees of Princeton University
7 #
8 # $Id: gen-static-content.py,v 1.35.2.1 2007/02/07 03:27:50 mlhuang Exp $
9 #
10
11 import os, sys, shutil
12 import time
13 import string
14 import codecs
15 import socket
16 import urllib2
17 import csv
18
19 SCRIPT_PID_FILE= "/var/run/gen-static-content.pid"
20
21 # where to store the generated files
22 GENERATED_OUTPUT_PATH= '/var/www/html/generated'
23
24 # this php block, if put at the top of the files,
25 # will enable them to be downloaded without the php
26 # engine parsing them
27 DISABLE_PHP_BLOCK= \
28 """<?php
29 if( isset($_GET['disablephp']) )
30   {
31     readfile(__FILE__);
32     exit();
33   }
34 ?>
35 """
36
37 # Globals
38 all_nodes = []
39 all_sites = []
40 node_group_nodes = {}
41
42 # return a php page that has node and site counts in it
43 def GetCountsFileContent(f):
44     f.write( DISABLE_PHP_BLOCK )
45     f.write( "<?php\n" )
46
47     node_count = len(all_nodes)
48     f.write( "$node_count= %s;\n" % node_count )
49     
50     site_count= len(all_sites)
51     f.write( "$site_count= %s;\n" % site_count )
52
53     f.write( "?>" )
54
55
56 # generate a plain text file in ~/.ssh/known_hosts format
57 def GetHostKeys(f):
58     time_generated= time.strftime("%a, %d %b %Y %H:%M:%S")
59
60     f.write( DISABLE_PHP_BLOCK )
61     
62     f.write( "<?php\n" )
63     f.write( "$node_list_generated_time= '%s';\n" % time_generated )
64     f.write( "header('Content-type: text/plain');\n" )
65     f.write( "?>\n" )
66
67     nodes = all_nodes
68
69     for node in all_nodes:
70         hostname = node['hostname']
71         ssh_rsa_key = node['ssh_rsa_key']
72         ip = node['ip']
73         if ssh_rsa_key:
74             if hostname:
75                 f.write( "%s %s\n" % (hostname, ssh_rsa_key) )
76             if ip:
77                 f.write( "%s %s\n" % (ip, ssh_rsa_key) )
78
79
80 # return php content that includes all the node lists
81 def GetNodeListsContent(f):
82     time_generated= time.strftime("%a, %d %b %Y %H:%M:%S")
83
84     f.write( DISABLE_PHP_BLOCK )
85     
86     f.write( "<?php\n" )
87     f.write( "$node_list_generated_time= '%s';\n" % time_generated )
88
89     # Nodes with primary IP addresses in boot state
90     nodes_in_boot = filter(lambda node: node['boot_state'] == "boot" and node['ip'],
91                            all_nodes)
92
93     # Hostnames
94     all_hosts = [node['hostname'] for node in nodes_in_boot]
95     f.write( "if( $which_node_list == 'all_hosts' )\n" )
96     f.write( "{\n" )
97     f.write( "?>\n" )
98     f.write( "\n".join(all_hosts) + "\n" )
99     f.write( "<?php\n" )
100     f.write( "}\n" )
101
102     # IPs
103     all_ips = [node['ip'] for node in nodes_in_boot]
104     f.write( "elseif( $which_node_list == 'all_ips' )\n" )
105     f.write( "{\n" )
106     f.write( "?>\n" )
107     f.write( "\n".join(all_ips) + "\n" )
108     f.write( "<?php\n" )
109     f.write( "}\n" )
110
111     # /etc/hosts entries
112     etc_hosts = [node['ip'] + "\t" + node['hostname'] for node in nodes_in_boot]
113     f.write( "elseif( $which_node_list == 'etc_hosts' )\n" )
114     f.write( "{\n" )
115     f.write( "?>\n" )
116     # Create a localhost entry for convenience
117     f.write( "127.0.0.1\tlocalhost.localdomain localhost\n" )
118     f.write( "\n".join(etc_hosts) + "\n" )
119     f.write( "<?php\n" )
120     f.write( "}\n" )
121
122     for group in ['Alpha', 'Beta']:
123         if not node_group_nodes.has_key(group):
124             node_group_nodes[group] = []
125
126         # Group nodes with primary IP addresses in boot state
127         group_nodes_in_boot = filter(lambda node: node['boot_state'] == "boot" and node['ip'],
128                                      node_group_nodes[group])
129
130         # Group hostnames
131         group_hosts = [node['hostname'] for node in group_nodes_in_boot]
132         f.write( "elseif( $which_node_list == '%s_hosts' )\n" % group.lower() )
133         f.write( "{\n" )
134         f.write( "?>\n" )
135         f.write( "\n".join(group_hosts) + "\n" )
136         f.write( "<?php\n" )
137         f.write( "}\n" )
138
139         # Group IPs
140         group_ips = [node['ip'] for node in group_nodes_in_boot]
141         f.write( "elseif( $which_node_list == '%s_ips' )\n" % group.lower() )
142         f.write( "{\n" )
143         f.write( "?>\n" )
144         f.write( "\n".join(group_ips) + "\n" )
145         f.write( "<?php\n" )
146         f.write( "}\n" )
147
148     # All production nodes (nodes not in Alpha or Beta)
149     production_nodes_in_boot = filter(lambda node: node not in node_group_nodes['Alpha'] and \
150                                                    node not in node_group_nodes['Beta'],
151                                       nodes_in_boot)
152
153     production_hosts = [node['hostname'] for node in production_nodes_in_boot]                           
154     f.write( "elseif( $which_node_list == 'production_hosts' )\n" )
155     f.write( "{\n" )
156     f.write( "?>\n" )
157     f.write( "\n".join(production_hosts) + "\n" )
158     f.write( "<?php\n" )
159     f.write( "}\n" )
160
161     production_ips = [node['ip'] for node in production_nodes_in_boot]                           
162     f.write( "elseif( $which_node_list == 'production_ips' )\n" )
163     f.write( "{\n" )
164     f.write( "?>\n" )
165     f.write( "\n".join(production_ips) + "\n" )
166     f.write( "<?php\n" )
167     f.write( "}\n" )
168     f.write( "?>" )
169
170
171 def GetPlanetFlowStats(f):
172     if hasattr(config, 'PLANETFLOW_BASE'):
173         url = "http://" + config.PLANETFLOW_BASE
174     else:
175         return
176
177     # Slices to calculate detailed statistics for
178     slices = [
179         'cmu_esm',
180         'cornell_beehive',
181         'cornell_cobweb',
182         'cornell_codons',
183         'michigan_tmesh',
184         'nyu_d',
185         'princeton_codeen',
186         'princeton_coblitz',
187         'princeton_comon',
188         'rice_epost',
189         'ucb_bamboo',
190         'ucb_i3',
191         'ucsd_sword',
192         'upenn_dharma',
193         'idsl_psepr',
194         'ucb_ganglia',
195         'cmu_irislog',
196         'tennessee_hliu'
197         ]
198
199     # Seconds to wait
200     socket.setdefaulttimeout(3600)
201
202     url = url + '/slice.php?csv=1&start_time=2+days+ago'
203     if slices:
204         url = url + '&slices[]=' + '&slices[]='.join(slices)
205     stats = urllib2.urlopen(url)
206     fields = ['slice', 'flows', 'packets', 'bytes', 'src_ips',
207               'dst_ips', 'top_dst_ip', 'top_dst_ip_bytes']
208     rows = csv.DictReader(stats, fields)
209     f.write("<?php\n")
210     f.write("$planetflow = array(\n")
211     for row in rows:
212         if row.has_key('slice'):
213             f.write("'%s' => array(\n" % row['slice'])
214             for field in fields:
215                 if row.has_key(field) and \
216                    row[field] is not None and \
217                    row[field] != "":
218                     if type(row[field]) == type(0):
219                         f.write("\t'%s' => %d,\n" % (field, int(row[field])))
220                     else:
221                         f.write("\t'%s' => '%s',\n" % (field, row[field]))
222             f.write("),\n")
223     f.write(");\n")
224     f.write("?>")
225
226
227
228 def GenDistMap():
229     # update the node distribution map
230     datadir = '/var/www/html/plot-latlong'
231
232     # plot-latlong looks for .mapinfo and .mapimages in $HOME
233     os.environ['HOME'] = datadir
234
235     if hasattr(config, 'PLC_WWW_MAPIMAGE'):
236         image = config.PLC_WWW_MAPIMAGE
237     else:
238         image = "World50"
239
240     (child_stdin,
241      child_stdout) = \
242      os.popen2('perl ' + datadir + os.sep + 'plot-latlong -m "%s" -s 3' % image)
243
244     for site in all_sites:
245         if site['latitude'] and site['longitude']:
246             child_stdin.write("%f %f\n" % \
247                               (site['latitude'], site['longitude']))
248     child_stdin.close()
249
250     map = file(GENERATED_OUTPUT_PATH + os.sep + image + '.png', 'w')
251     map.write(child_stdout.read())
252     child_stdout.close()
253     map.close()
254
255
256 # which files to generate, and the functions in
257 # this script to call to get the content for
258 STATIC_FILE_LIST= (
259     ('_gen_counts.php',GetCountsFileContent),
260     ('_gen_node_lists.php',GetNodeListsContent),
261     ('_gen_known_hosts.php',GetHostKeys),
262     ('_gen_planetflow.php',GetPlanetFlowStats),
263     (None,GenDistMap)
264     )
265
266
267 if __name__ == '__main__':
268
269     # see if we are already running by checking the existance
270     # of a PID file, and if it exists, attempting a test kill
271     # to see if the process really does exist. If both of these
272     # tests pass, exit.
273         
274     if os.access(SCRIPT_PID_FILE, os.R_OK):
275         pid= string.strip(file(SCRIPT_PID_FILE).readline())
276         if pid <> "":
277             if os.system("/bin/kill -0 %s > /dev/null 2>&1" % pid) == 0:
278                 sys.exit(0)
279             
280     # write out our process id
281     pidfile= file( SCRIPT_PID_FILE, 'w' )
282     pidfile.write( "%d\n" % os.getpid() )
283     pidfile.close()
284     pidfile= None
285
286     # Get all nodes and sites
287     begin()
288     GetNodes(None, ['node_id', 'hostname', 'boot_state', 'ssh_rsa_key', 'interface_ids'])
289     GetInterfaces(None, ['interface_id', 'ip', 'is_primary'])
290     GetSites(None, ['site_id', 'latitude', 'longitude'])
291     GetNodeGroups(None, ['nodegroup_id', 'tagname', 'node_ids'])
292     (all_nodes, all_nodenetworks, all_sites, all_groups) = commit()
293
294     all_nodenetworks = dict([(nodenetwork['interface_id'], nodenetwork) \
295                              for nodenetwork in all_nodenetworks])
296
297     # Set primary IP, if any
298     for node in all_nodes:
299         node['ip'] = None
300         for interface_id in node['interface_ids']:
301             try:
302                 nodenetwork = all_nodenetworks[interface_id]
303                 if nodenetwork['is_primary']:
304                     node['ip'] = nodenetwork['ip']
305                 break
306             except IndexError, KeyError:
307                 continue
308
309     # Get list of nodes in each node group
310     for group in all_groups:
311         nodes_in_group = filter(lambda node: node['node_id'] in group['node_ids'], all_nodes)
312         node_group_nodes[group['tagname']] = nodes_in_group
313
314     # generate the static content files
315     for (file_name,func) in STATIC_FILE_LIST:
316         if file_name is not None:
317             try:
318                 output_file_path= "%s/%s" % (GENERATED_OUTPUT_PATH,file_name)
319                 tmp_output_file_path= output_file_path + '.tmp'
320                 tmp_output_file= codecs.open( tmp_output_file_path, encoding = 'utf-8', mode = "w" )
321             except IOError, err:
322                 print( "Unable to open file %s for writing." % output_file_path )
323                 continue
324
325             try:
326                 func(tmp_output_file)
327                 tmp_output_file.flush()
328                 shutil.copyfile( tmp_output_file_path, output_file_path )
329             except Exception, e:
330                 print "Unable to get content for file: %s" % file_name, e
331                 import traceback
332                 traceback.print_exc()
333
334             tmp_output_file.close()
335             tmp_output_file= None
336             os.unlink( tmp_output_file_path )
337         else:
338             func()
339
340     # remove the PID file
341     os.unlink( SCRIPT_PID_FILE )