5 # Mark Huang <mlhuang@cs.princeton.edu>
6 # Copyright (C) 2006 The Trustees of Princeton University
12 from xml.sax.saxutils import escape, quoteattr, XMLGenerator
14 PID_FILE= "/var/run/all_planetlab_xml.pid"
17 # Web server document root
19 DOCROOT = '/var/www/html/xml'
22 # DTD and version number for site information
32 print "Usage: %s [OPTION]..." % sys.argv[0]
34 print " -n, --dryrun Dry run, do not write files (default: %s)" % dryrun
35 print " -d, --docroot=DIR Document root (default: %s)" % DOCROOT
36 print " -h, --help This message"
41 (opts, argv) = getopt.getopt(sys.argv[1:], "nd:h", ["dryrun", "docroot=", "help"])
42 except getopt.GetoptError, err:
43 print "Error: " + err.msg
46 for (opt, optval) in opts:
47 if opt == "-n" or opt == "--dryrun":
49 elif opt == "-d" or opt == "--docroot":
56 if os.access(PID_FILE, os.R_OK):
57 pid= file(PID_FILE).readline().strip()
59 if os.system("/bin/kill -0 %s > /dev/null 2>&1" % pid) == 0:
62 # write out our process id
63 pidfile= file( PID_FILE, 'w' )
64 pidfile.write( "%d\n" % os.getpid() )
67 # Load shell with default configuration
68 sys.path.append('/usr/share/plc_api')
69 from PLC.Shell import Shell
70 plc = Shell(globals())
73 # Get information from API
77 GetNodes(None, ['node_id', 'model', 'boot_state', 'hostname', 'version', 'ssh_rsa_key', 'interface_ids', 'slice_ids_whitelist'])
78 GetInterfaces({'is_primary': True}, ['interface_id', 'node_id', 'ip', 'mac', 'bwlimit'])
79 GetSites(None, ['name', 'latitude', 'longitude', 'url', 'site_id', 'login_base', 'abbreviated_name', 'node_ids'])
80 GetNodeGroups(['Alpha', 'Beta', 'Rollout', 'Production'], ['groupname', 'node_ids'])
81 (nodes, nodenetworks, sites, groups) = commit()
83 # remove whitelisted nodes
84 remove_whitelisted = lambda node: not node['slice_ids_whitelist']
85 nodes = filter(remove_whitelisted, nodes)
87 nodes = dict([(node['node_id'], node) for node in nodes])
89 for nodenetwork in nodenetworks:
90 if nodes.has_key(nodenetwork['node_id']):
91 node = nodes[nodenetwork['node_id']]
92 for key, value in nodenetwork.iteritems():
95 group_node_ids = dict([(group['groupname'], group['node_ids']) for group in groups])
97 class PrettyXMLGenerator(XMLGenerator):
99 Adds indentation to the beginning and newlines to the end of
100 opening and closing tags.
103 def __init__(self, out = sys.stdout, encoding = "utf-8", indent = "", addindent = "", newl = ""):
104 XMLGenerator.__init__(self, out, encoding)
105 # XMLGenerator does not export _write()
106 self.write = self.ignorableWhitespace
107 self.indents = [indent]
108 self.addindent = addindent
111 def startDocument(self):
112 XMLGenerator.startDocument(self)
114 def startElement(self, name, attrs, indent = True, newl = True):
116 self.ignorableWhitespace("".join(self.indents))
117 self.indents.append(self.addindent)
119 XMLGenerator.startElement(self, name, attrs)
122 self.ignorableWhitespace(self.newl)
124 def characters(self, content):
127 self.write(escape(content, {
132 def endElement(self, name, indent = True, newl = True):
135 self.ignorableWhitespace("".join(self.indents))
137 XMLGenerator.endElement(self, name)
140 self.ignorableWhitespace(self.newl)
142 def simpleElement(self, name, attrs = {}, indent = True, newl = True):
144 self.ignorableWhitespace("".join(self.indents))
146 self.write('<' + name)
147 for (name, value) in attrs.items():
148 self.write(' %s=%s' % (name, quoteattr(value.strip())))
152 self.ignorableWhitespace(self.newl)
155 # Write out sites.xml
159 sites_xml = sys.stdout
161 sites_xml = open(DOCROOT + "/sites.xml", mode = "w")
163 xml = PrettyXMLGenerator(out = sites_xml, encoding = ENCODING, indent = "", addindent = " ", newl = "\n")
166 # Write embedded DTD verbatim
167 xml.ignorableWhitespace("""
168 <!DOCTYPE PLANETLAB_SITES [
169 <!ELEMENT PLANETLAB_SITES (SITE)*>
170 <!ATTLIST PLANETLAB_SITES VERSION CDATA #REQUIRED
171 TIME CDATA #REQUIRED>
173 <!ELEMENT SITE (HOST)*>
174 <!ATTLIST SITE NAME CDATA #REQUIRED
175 LATITUDE CDATA #REQUIRED
176 LONGITUDE CDATA #REQUIRED
178 SITE_ID CDATA #REQUIRED
179 LOGIN_BASE CDATA #REQUIRED
180 FULL_SITE_NAME CDATA #REQUIRED
181 SHORT_SITE_NAME CDATA #REQUIRED
184 <!ELEMENT HOST EMPTY>
185 <!ATTLIST HOST NAME CDATA #REQUIRED
187 MODEL CDATA #REQUIRED
190 VERSION CDATA #REQUIRED
191 NODE_ID CDATA #REQUIRED
192 BOOT_VERSION CDATA ""
194 BOOT_STATE CDATA #REQUIRED
201 def format_tc_rate(rate):
203 Formats a bits/second rate into a tc rate string
206 if rate >= 1000000000 and (rate % 1000000000) == 0:
207 return "%.0fgbit" % (rate / 1000000000.)
208 elif rate >= 1000000 and (rate % 1000000) == 0:
209 return "%.0fmbit" % (rate / 1000000.)
211 return "%.0fkbit" % (rate / 1000.)
213 return "%.0fbit" % rate
215 # <PLANETLAB_SITES VERSION="major.minor" TIME="seconds_since_epoch">
216 xml.startElement('PLANETLAB_SITES', {'VERSION': SITE_VERSION,
217 'TIME': str(int(time.time()))})
222 for attr in ['name', 'latitude', 'longitude', 'url', 'site_id', 'login_base']:
223 attrs[attr.upper()] = unicode(site[attr])
224 attrs['FULL_SITE_NAME'] = unicode(site['name'])
225 attrs['SHORT_SITE_NAME'] = unicode(site['abbreviated_name'])
226 xml.startElement('SITE', attrs)
228 for node_id in site['node_ids']:
229 if nodes.has_key(node_id):
230 node = nodes[node_id]
234 attrs['NAME'] = unicode(node['hostname'])
235 attrs['VERSION'] = "2.0"
236 for attr in ['model', 'node_id', 'boot_state']:
237 attrs[attr.upper()] = unicode(node[attr]).strip()
239 # If the node is in Alpha, Beta, or Rollout, otherwise Production
240 for group in ['Alpha', 'Beta', 'Rollout', 'Production']:
241 if group_node_ids.has_key(group) and \
242 node_id in group_node_ids[group]:
244 attrs['STATUS'] = group
247 attrs['BOOT_VERSION'] = unicode(node['version'].splitlines()[0])
248 if node['ssh_rsa_key']:
249 attrs['RSA_KEY'] = unicode(node['ssh_rsa_key'].splitlines()[0])
251 if node.has_key('ip') and node['ip']:
252 attrs['IP'] = unicode(node['ip'])
253 if node.has_key('mac') and node['mac']:
254 attrs['MAC'] = unicode(node['mac'])
255 if node.has_key('bwlimit') and node['bwlimit']:
256 attrs['BWLIMIT'] = unicode(format_tc_rate(node['bwlimit']))
258 xml.simpleElement('HOST', attrs)
261 xml.endElement('SITE')
263 xml.endElement('PLANETLAB_SITES')
266 # remove the PID file
267 os.unlink( PID_FILE )