--- /dev/null
+#!/usr/bin/python
+#
+# Generates a DocBook section documenting all PLCAPI methods on
+# stdout.
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+# $Id: DocBook.py 9269 2008-05-08 09:11:02Z thierry $
+#
+
+import xml.dom.minidom
+from xml.dom.minidom import Element, Text
+import codecs
+
+from PLC.Parameter import Parameter, Mixed, xmlrpc_type, python_type
+
+# xml.dom.minidom.Text.writexml adds surrounding whitespace to textual
+# data when pretty-printing. Override this behavior.
+class TrimText(Text):
+ """text"""
+ def __init__(self, text = None):
+ self.data = unicode(text)
+
+ def writexml(self, writer, indent="", addindent="", newl=""):
+ Text.writexml(self, writer, "", "", "")
+
+class TrimTextElement(Element):
+ """<tagName>text</tagName>"""
+ def __init__(self, tagName, text = None):
+ Element.__init__(self, tagName)
+ if text is not None:
+ self.appendChild(TrimText(text))
+
+ def writexml(self, writer, indent="", addindent="", newl=""):
+ writer.write(indent)
+ Element.writexml(self, writer, "", "", "")
+ writer.write(newl)
+
+class simpleElement(TrimTextElement): pass
+
+class paraElement(simpleElement):
+ """<para>text</para>"""
+ def __init__(self, text = None):
+ simpleElement.__init__(self, 'para', text)
+
+class blockquoteElement(Element):
+ """<blockquote><para>text...</para><para>...text</para></blockquote>"""
+ def __init__(self, text = None):
+ Element.__init__(self, 'blockquote')
+ if text is not None:
+ # Split on blank lines
+ lines = [line.strip() for line in text.strip().split("\n")]
+ lines = "\n".join(lines)
+ paragraphs = lines.split("\n\n")
+
+ for paragraph in paragraphs:
+ self.appendChild(paraElement(paragraph))
+
+def param_type(param):
+ """Return the XML-RPC type of a parameter."""
+ if isinstance(param, Mixed) and len(param):
+ subtypes = [param_type(subparam) for subparam in param]
+ return " or ".join(subtypes)
+ elif isinstance(param, (list, tuple, set)) and len(param):
+ return "array of " + " or ".join([param_type(subparam) for subparam in param])
+ else:
+ return xmlrpc_type(python_type(param))
+
+class paramElement(Element):
+ """An optionally named parameter."""
+ def __init__(self, name, param):
+ # <listitem>
+ Element.__init__(self, 'listitem')
+
+ description = Element('para')
+
+ if name:
+ description.appendChild(simpleElement('parameter', name))
+ description.appendChild(TrimText(": "))
+
+ description.appendChild(TrimText(param_type(param)))
+
+ if isinstance(param, (list, tuple, set)) and len(param) == 1:
+ param = param[0]
+
+ if isinstance(param, Parameter):
+ description.appendChild(TrimText(", " + param.doc))
+ param = param.type
+
+ self.appendChild(description)
+
+ if isinstance(param, dict):
+ itemizedlist = Element('itemizedlist')
+ self.appendChild(itemizedlist)
+ for name, subparam in param.iteritems():
+ itemizedlist.appendChild(paramElement(name, subparam))
+
+ elif isinstance(param, (list, tuple, set)) and len(param):
+ itemizedlist = Element('itemizedlist')
+ self.appendChild(itemizedlist)
+ for subparam in param:
+ itemizedlist.appendChild(paramElement(None, subparam))
+
+class DocBook:
+
+ def __init__ (self,functions_list):
+ self.functions_list = functions_list
+
+ def Process (self):
+
+ for func in self.functions_list:
+ method = func.name
+
+ if func.status == "deprecated":
+ continue
+
+ (min_args, max_args, defaults) = func.args()
+
+ section = Element('section')
+ section.setAttribute('id', func.name)
+ section.appendChild(simpleElement('title', func.name))
+
+ prototype = "%s (%s)" % (method, ", ".join(max_args))
+ para = paraElement('Prototype:')
+ para.appendChild(blockquoteElement(prototype))
+ section.appendChild(para)
+
+ para = paraElement('Description:')
+ para.appendChild(blockquoteElement(func.__doc__))
+ section.appendChild(para)
+
+ para = paraElement('Allowed Roles:')
+ para.appendChild(blockquoteElement(", ".join(func.roles)))
+ section.appendChild(para)
+
+ section.appendChild(paraElement('Parameters:'))
+ params = Element('itemizedlist')
+ if func.accepts:
+ for name, param, default in zip(max_args, func.accepts, defaults):
+ params.appendChild(paramElement(name, param))
+ else:
+ listitem = Element('listitem')
+ listitem.appendChild(paraElement('None'))
+ params.appendChild(listitem)
+ section.appendChild(params)
+
+ section.appendChild(paraElement('Returns:'))
+ returns = Element('itemizedlist')
+ returns.appendChild(paramElement(None, func.returns))
+ section.appendChild(returns)
+
+ print section.toprettyxml(encoding = "UTF-8")
--- /dev/null
+#
+# (Re)builds API documentation
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+# $Id: Makefile 9346 2008-05-14 01:44:39Z thierry $
+#
+
+all: PLCAPI.html
+
+# XML - as opposed to SGML - requires an identifier - see
+# http://www.docbook.org/tdg/en/html/appb.html
+# and, openjade opens http connections when using the official URL
+# as an identifier; this is slow, and sometimes fails and breaks the build
+
+# locating locally installed docbook43 dtd - fedora-specific
+remote-docbook-43 = http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd
+local-docbook-43 = $(wildcard /usr/share/sgml/docbook/xml-dtd-4.3-*/docbookx.dtd)
+docbook-43=$(if $(local-docbook-43),$(local-docbook-43),$(remote-docbook-43))
+
+PLCAPI.xml: PLCAPI.xml.in
+ $(if $(local-docbook-43), \
+ echo Using locally installed DTD $(local-docbook-43), \
+ echo WARNING - could not locate local DTD - using remote $(remote-docbook-43))
+ sed -e s:@DOCBOOK-43@:$(docbook-43): $< > $@
+
+.PLCAPI.xml.valid: Methods.xml
+
+API_SOURCES = ../PLC/__init__.py ../PLC/Methods/__init__.py
+
+Methods.xml: DocBook.py DocBookLocal.py $(API_SOURCES)
+ PYTHONPATH=.. ./DocBookLocal.py > $@
+
+#
+# Documentation
+#
+
+# Validate the XML
+.%.xml.valid: %.xml
+ xmllint --valid --output $@ $<
+
+# Remove the temporary output file after compilation
+.SECONDARY: .%.xml.valid
+
+# Compile it into other formats
+FORMATS := dvi html man ps pdf rtf tex texi txt
+
+DOCBOOK2FLAGS := -V biblio-number=1
+
+define docbook2
+%.$(1): %.xml .%.xml.valid
+ docbook2$(1) --nochunks $$(DOCBOOK2FLAGS) $$<
+endef
+
+$(foreach format,$(FORMATS),$(eval $(call docbook2,$(format))))
+
+clean:
+ rm -f $(patsubst %,*.%,$(FORMATS)) .*.xml.valid Methods.xml
+
+.PHONY: clean all
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"@DOCBOOK-43@" [
+<!ENTITY Methods SYSTEM "Methods.xml">
+]>
+
+<book>
+ <bookinfo>
+ <title>PlanetLab Central API Documentation</title>
+ </bookinfo>
+
+ <chapter id="Introduction">
+ <title>Introduction</title>
+
+ <para>The PlanetLab Central API (PLCAPI) is the interface through
+ which the PlanetLab Central database should be accessed and
+ maintained. The API is used by the website, by nodes, by automated
+ scripts, and by users to access and update information about
+ users, nodes, sites, slices, and other entities maintained by the
+ database.</para>
+
+ <section id="Authentication">
+ <title>Authentication</title>
+
+ <para>The API should be accessed via XML-RPC over HTTPS. The API
+ supports the standard introspection calls <link
+ linkend="system.listMethods">system.listMethods</link>, <link
+ linkend="system.methodSignature">system.methodSignature</link>,
+ and <link linkend="system.methodHelp">system.methodHelp</link>,
+ and the standard batching call <link
+ linkend="system.multicall">system.multicall</link>. With the
+ exception of these calls, all PLCAPI calls take an
+ authentication structure as their first argument. All
+ authentication structures require the specification of
+ <parameter>AuthMethod</parameter>. If the documentation for a
+ call does not further specify the authentication structure, then
+ any of (but only) the following authentication structures may be
+ used:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>Session authentication. User sessions are typically
+ valid for 24 hours. Node sessions are valid until the next
+ reboot. Obtain a session key with <link
+ linkend="GetSession">GetSession</link> using another form of
+ authentication, such as password or GnuPG
+ authentication.</para>
+ <informaltable frame="none" rules="rows">
+ <tgroup cols="3">
+ <tbody>
+ <row><entry>AuthMethod</entry><entry><literal>session</literal></entry></row>
+ <row><entry>session</entry><entry>Session key</entry></row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </listitem>
+ <listitem>
+ <para>Password authentication.</para>
+ <informaltable frame="none" rules="rows">
+ <tgroup cols="3">
+ <tbody>
+ <row><entry>AuthMethod</entry><entry><literal>password</literal></entry></row>
+ <row><entry>Username</entry><entry>Username, typically an e-mail address</entry></row>
+ <row><entry>AuthString</entry><entry>Authentication string, typically a password</entry></row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </listitem>
+ <listitem>
+ <para>GnuPG authentication. Users may upload a GPG public key
+ using <link linkend="AddPersonKey">AddPersonKey</link>. Peer
+ GPG keys should be added with <link
+ linkend="AddPeer">AddPeer</link> or <link
+ linkend="UpdatePeer">UpdatePeer</link>.
+ </para>
+ <informaltable frame="none" rules="rows">
+ <tgroup cols="3">
+ <tbody>
+ <row><entry>AuthMethod</entry><entry><literal>gpg</literal></entry></row>
+ <row><entry>name</entry><entry>Peer or user name</entry></row>
+ <row><entry>signature</entry><entry>GnuPG signature of
+ the <ulink
+ url="http://www.w3.org/TR/xml-c14n">canonicalized</ulink>
+ <ulink url="http://www.xmlrpc.com/spec">XML-RPC</ulink>
+ representation of the rest of the arguments to the
+ call.</entry></row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </listitem>
+ <listitem>
+ <para>Anonymous authentication.</para>
+ <informaltable frame="none" rules="rows">
+ <tgroup cols="3">
+ <tbody>
+ <row><entry>AuthMethod</entry><entry><literal>anonymous</literal></entry></row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </listitem>
+ </itemizedlist>
+ </section>
+
+ <section id="Roles">
+ <title>Roles</title>
+
+ <para>Some functions may only be called by users with certain
+ roles (see <link linkend="GetRoles">GetRoles</link>), and others
+ may return different information to different callers depending
+ on the role(s) of the caller.</para>
+
+ <para>The <literal>node</literal> and
+ <literal>anonymous</literal> roles are pseudo-roles. A function
+ that allows the <literal>node</literal> role may be called by
+ automated scripts running on a node, such as the Boot and Node
+ Managers. A function that allows the
+ <literal>anonymous</literal> role may be called by anyone; an
+ API authentication structure must still be specified (see <xref
+ linkend="Authentication"/>).</para>
+ </section>
+
+ <section id="Filters">
+ <title>Filters</title>
+
+ <para>Most of the <function>Get</function> functions take a
+ filter argument. Filters may be arrays of integer (and sometimes
+ string) identifiers, or a struct representing a filter on the
+ attributes of the entities being queried. For example,</para>
+
+<programlisting>
+# plcsh code fragment (see below)
+GetNodes([1,2,3])
+GetNodes({'node_id': [1,2,3]})
+</programlisting>
+
+ <para>Would be equivalent queries. Attributes that are
+ themselves arrays (such as <literal>nodenetwork_ids</literal>
+ and <literal>slice_ids</literal> for nodes) cannot be used in
+ filters.</para>
+
+ <para> Filters support a few extra features illustrated in the following examples.</para>
+ <itemizedlist>
+ <listitem>
+ <para> <emphasis> Pattern Matching </emphasis> </para>
+ <programlisting>GetNodes ( { 'hostname' : '*.fr' } ) </programlisting>
+ </listitem>
+ <listitem>
+ <para> <emphasis> Negation </emphasis> </para>
+ <programlisting>GetNodes( { '~peer_id' : None } ) </programlisting>
+ </listitem>
+ <listitem>
+ <para> <emphasis> Numeric comparisons </emphasis> </para>
+ <programlisting>GetEvents( { '>time' : 1178531418 } ) </programlisting>
+ <programlisting>GetEvents( { ']event_id' : 2305 } ) </programlisting>
+ </listitem>
+ <listitem>
+ <para> <emphasis> Sorting and Clipping </emphasis> </para>
+ <programlisting> GetNodes( { '-SORT' : 'hostname' , '-OFFSET' : 30 , '-LIMIT' : 25 }</programlisting>
+ </listitem>
+ </itemizedlist>
+ </section>
+
+ <section>
+ <title>PlanetLab shell</title>
+
+ <para>A command-line program called <command>plcsh</command>
+ simplifies authentication structure handling, and is useful for
+ scripting. This program is distributed as a Linux RPM called
+ PLCAPI and requires Python ≥2.4.</para>
+
+ <programlisting>
+usage: plcsh [options]
+
+options:
+ -f CONFIG, --config=CONFIG
+ PLC configuration file
+ -h URL, --url=URL API URL
+ -c CACERT, --cacert=CACERT
+ API SSL certificate
+ -k INSECURE, --insecure=INSECURE
+ Do not check SSL certificate
+ -m METHOD, --method=METHOD
+ API authentication method
+ -s SESSION, --session=SESSION
+ API session key
+ -u USER, --user=USER API user name
+ -p PASSWORD, --password=PASSWORD
+ API password
+ -r ROLE, --role=ROLE API role
+ -x, --xmlrpc Use XML-RPC interface
+ --help show this help message and exit
+ </programlisting>
+
+ <para>Specify at least the API URL and your user name:</para>
+
+ <programlisting>
+plcsh --url https://www.planet-lab.org/PLCAPI/ -u user@site.edu
+ </programlisting>
+
+ <para>You will be presented with a prompt. From here, you can
+ invoke API calls and omit the authentication structure, as it will
+ be filled in automatically.</para>
+
+ <programlisting>
+user@site.edu connected using password authentication
+Type "system.listMethods()" or "help(method)" for more information.
+[user@site.edu]>>> AuthCheck()
+1
+[user@site.edu]>>> GetNodes([121], ['node_id', 'hostname'])
+[{'node_id': 121, 'hostname': 'planetlab-1.cs.princeton.edu'}]
+ </programlisting>
+
+ <para>As this program is actually a Python interpreter, you may
+ create variables, execute for loops, import other packages, etc.,
+ directly on the command line as you would using the regular Python
+ shell.</para>
+
+ <para>To use <command>plcsh</command> programmatically, import
+ the <function>PLC.Shell</function> module:</para>
+
+ <programlisting>
+#!/usr/bin/python
+
+import sys
+
+# Default location that the PLCAPI RPM installs the PLC class
+sys.path.append('/usr/share/plc_api')
+
+# Initialize shell environment. Shell() will define all PLCAPI methods
+# in the specified namespace (specifying globals() will define them
+# globally).
+from PLC.Shell import Shell
+plc = Shell(globals(),
+ url = "https://www.planet-lab.org/PLCAPI/",
+ user = "user@site.edu",
+ password = "password")
+
+# Both are equivalent
+nodes = GetNodes([121], ['node_id', 'hostname'])
+nodes = plc.GetNodes([121], ['node_id', 'hostname'])
+ </programlisting>
+ </section>
+ </chapter>
+
+ <chapter id="Methods">
+ <title>PlanetLab API Methods</title>
+ <para></para>
+
+ &Methods;
+ </chapter>
+
+</book>
+
+<!-- LocalWords: PlanetLab API PLCAPI RPC HTTPS listMethods methodSignature
+-->
+<!-- LocalWords: methodHelp multicall AuthMethod GetSession GnuPG Username GPG
+-->
+<!-- LocalWords: AuthString AddPersonKey AddPeer UpdatePeer gpg
+-->