5 # Mark Huang <mlhuang@cs.princeton.edu>
6 # Copyright (C) 2006 The Trustees of Princeton University
8 # $Id: gen-sites-xml.py,v 1.8 2007/09/14 20:08:28 tmack Exp $
14 from xml.sax.saxutils import escape, quoteattr, XMLGenerator
16 PID_FILE= "/var/run/all_planetlab_xml.pid"
19 # Web server document root
21 DOCROOT = '/var/www/html/xml'
24 # DTD and version number for site information
34 print "Usage: %s [OPTION]..." % sys.argv[0]
36 print " -n, --dryrun Dry run, do not write files (default: %s)" % dryrun
37 print " -d, --docroot=DIR Document root (default: %s)" % DOCROOT
38 print " -h, --help This message"
43 (opts, argv) = getopt.getopt(sys.argv[1:], "nd:h", ["dryrun", "docroot=", "help"])
44 except getopt.GetoptError, err:
45 print "Error: " + err.msg
48 for (opt, optval) in opts:
49 if opt == "-n" or opt == "--dryrun":
51 elif opt == "-d" or opt == "--docroot":
58 if os.access(PID_FILE, os.R_OK):
59 pid= file(PID_FILE).readline().strip()
61 if os.system("/bin/kill -0 %s > /dev/null 2>&1" % pid) == 0:
64 # write out our process id
65 pidfile= file( PID_FILE, 'w' )
66 pidfile.write( "%d\n" % os.getpid() )
69 # Load shell with default configuration
70 sys.path.append('/usr/share/plc_api')
71 from PLC.Shell import Shell
72 plc = Shell(globals())
75 # Get information from API
79 GetNodes(None, ['node_id', 'model', 'boot_state', 'hostname', 'version', 'ssh_rsa_key', 'nodenetwork_ids', 'slice_ids_whitelist'])
80 GetNodeNetworks({'is_primary': True}, ['nodenetwork_id', 'node_id', 'ip', 'mac', 'bwlimit'])
81 GetSites(None, ['name', 'latitude', 'longitude', 'url', 'site_id', 'login_base', 'abbreviated_name', 'node_ids'])
82 GetNodeGroups(['Alpha', 'Beta', 'Rollout', 'Production'], ['name', 'node_ids'])
83 (nodes, nodenetworks, sites, groups) = commit()
85 # remove whitelisted nodes
86 remove_whitelisted = lambda node: not node['slice_ids_whitelist']
87 nodes = filter(remove_whitelisted, nodes)
89 nodes = dict([(node['node_id'], node) for node in nodes])
91 for nodenetwork in nodenetworks:
92 if nodes.has_key(nodenetwork['node_id']):
93 node = nodes[nodenetwork['node_id']]
94 for key, value in nodenetwork.iteritems():
97 group_node_ids = dict([(group['name'], group['node_ids']) for group in groups])
99 class PrettyXMLGenerator(XMLGenerator):
101 Adds indentation to the beginning and newlines to the end of
102 opening and closing tags.
105 def __init__(self, out = sys.stdout, encoding = "utf-8", indent = "", addindent = "", newl = ""):
106 XMLGenerator.__init__(self, out, encoding)
107 # XMLGenerator does not export _write()
108 self.write = self.ignorableWhitespace
109 self.indents = [indent]
110 self.addindent = addindent
113 def startDocument(self):
114 XMLGenerator.startDocument(self)
116 def startElement(self, name, attrs, indent = True, newl = True):
118 self.ignorableWhitespace("".join(self.indents))
119 self.indents.append(self.addindent)
121 XMLGenerator.startElement(self, name, attrs)
124 self.ignorableWhitespace(self.newl)
126 def characters(self, content):
129 self.write(escape(content, {
134 def endElement(self, name, indent = True, newl = True):
137 self.ignorableWhitespace("".join(self.indents))
139 XMLGenerator.endElement(self, name)
142 self.ignorableWhitespace(self.newl)
144 def simpleElement(self, name, attrs = {}, indent = True, newl = True):
146 self.ignorableWhitespace("".join(self.indents))
148 self.write('<' + name)
149 for (name, value) in attrs.items():
150 self.write(' %s=%s' % (name, quoteattr(value.strip())))
154 self.ignorableWhitespace(self.newl)
157 # Write out sites.xml
161 sites_xml = sys.stdout
163 sites_xml = open(DOCROOT + "/sites.xml", mode = "w")
165 xml = PrettyXMLGenerator(out = sites_xml, encoding = ENCODING, indent = "", addindent = " ", newl = "\n")
168 # Write embedded DTD verbatim
169 xml.ignorableWhitespace("""
170 <!DOCTYPE PLANETLAB_SITES [
171 <!ELEMENT PLANETLAB_SITES (SITE)*>
172 <!ATTLIST PLANETLAB_SITES VERSION CDATA #REQUIRED
173 TIME CDATA #REQUIRED>
175 <!ELEMENT SITE (HOST)*>
176 <!ATTLIST SITE NAME CDATA #REQUIRED
177 LATITUDE CDATA #REQUIRED
178 LONGITUDE CDATA #REQUIRED
180 SITE_ID CDATA #REQUIRED
181 LOGIN_BASE CDATA #REQUIRED
182 FULL_SITE_NAME CDATA #REQUIRED
183 SHORT_SITE_NAME CDATA #REQUIRED
186 <!ELEMENT HOST EMPTY>
187 <!ATTLIST HOST NAME CDATA #REQUIRED
189 MODEL CDATA #REQUIRED
192 VERSION CDATA #REQUIRED
193 NODE_ID CDATA #REQUIRED
194 BOOT_VERSION CDATA ""
196 BOOT_STATE CDATA #REQUIRED
203 def format_tc_rate(rate):
205 Formats a bits/second rate into a tc rate string
208 if rate >= 1000000000 and (rate % 1000000000) == 0:
209 return "%.0fgbit" % (rate / 1000000000.)
210 elif rate >= 1000000 and (rate % 1000000) == 0:
211 return "%.0fmbit" % (rate / 1000000.)
213 return "%.0fkbit" % (rate / 1000.)
215 return "%.0fbit" % rate
217 # <PLANETLAB_SITES VERSION="major.minor" TIME="seconds_since_epoch">
218 xml.startElement('PLANETLAB_SITES', {'VERSION': SITE_VERSION,
219 'TIME': str(int(time.time()))})
224 for attr in ['name', 'latitude', 'longitude', 'url', 'site_id', 'login_base']:
225 attrs[attr.upper()] = unicode(site[attr])
226 attrs['FULL_SITE_NAME'] = unicode(site['name'])
227 attrs['SHORT_SITE_NAME'] = unicode(site['abbreviated_name'])
228 xml.startElement('SITE', attrs)
230 for node_id in site['node_ids']:
231 if nodes.has_key(node_id):
232 node = nodes[node_id]
236 attrs['NAME'] = unicode(node['hostname'])
237 attrs['VERSION'] = "2.0"
238 for attr in ['model', 'node_id', 'boot_state']:
239 attrs[attr.upper()] = unicode(node[attr]).strip()
241 # If the node is in Alpha, Beta, or Rollout, otherwise Production
242 for group in ['Alpha', 'Beta', 'Rollout', 'Production']:
243 if group_node_ids.has_key(group) and \
244 node_id in group_node_ids[group]:
246 attrs['STATUS'] = group
249 attrs['BOOT_VERSION'] = unicode(node['version'].splitlines()[0])
250 if node['ssh_rsa_key']:
251 attrs['RSA_KEY'] = unicode(node['ssh_rsa_key'].splitlines()[0])
253 if node.has_key('ip') and node['ip']:
254 attrs['IP'] = unicode(node['ip'])
255 if node.has_key('mac') and node['mac']:
256 attrs['MAC'] = unicode(node['mac'])
257 if node.has_key('bwlimit') and node['bwlimit']:
258 attrs['BWLIMIT'] = unicode(format_tc_rate(node['bwlimit']))
260 xml.simpleElement('HOST', attrs)
263 xml.endElement('SITE')
265 xml.endElement('PLANETLAB_SITES')
268 # remove the PID file
269 os.unlink( PID_FILE )