From 42df14f4fe53b13d9705422ae6b54daa67b6c6f3 Mon Sep 17 00:00:00 2001 From: Tony Mack Date: Fri, 10 Oct 2008 19:16:33 +0000 Subject: [PATCH] starting new development to support slice conf files --- doc/DocBook.py | 153 ++++++++++++++++++++++++++ doc/DocBookLocal.py | 11 ++ doc/Makefile | 61 +++++++++++ doc/PLCAPI.xml.in | 259 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 484 insertions(+) create mode 100755 doc/DocBook.py create mode 100755 doc/DocBookLocal.py create mode 100644 doc/Makefile create mode 100644 doc/PLCAPI.xml.in diff --git a/doc/DocBook.py b/doc/DocBook.py new file mode 100755 index 00000000..325a2c91 --- /dev/null +++ b/doc/DocBook.py @@ -0,0 +1,153 @@ +#!/usr/bin/python +# +# Generates a DocBook section documenting all PLCAPI methods on +# stdout. +# +# Mark Huang +# 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): + """text""" + 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): + """text""" + def __init__(self, text = None): + simpleElement.__init__(self, 'para', text) + +class blockquoteElement(Element): + """
text......text
""" + 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): + # + 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") diff --git a/doc/DocBookLocal.py b/doc/DocBookLocal.py new file mode 100755 index 00000000..f1624fd8 --- /dev/null +++ b/doc/DocBookLocal.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +from PLC.API import PLCAPI +from DocBook import DocBook + +def api_methods(): + api = PLCAPI(None) + methods = api.methods + return [api.callable(method) for method in methods] + +DocBook(api_methods ()).Process() diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..c482ccda --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,61 @@ +# +# (Re)builds API documentation +# +# Mark Huang +# 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 diff --git a/doc/PLCAPI.xml.in b/doc/PLCAPI.xml.in new file mode 100644 index 00000000..ffc0057a --- /dev/null +++ b/doc/PLCAPI.xml.in @@ -0,0 +1,259 @@ + + +]> + + + + PlanetLab Central API Documentation + + + + Introduction + + 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. + +
+ Authentication + + The API should be accessed via XML-RPC over HTTPS. The API + supports the standard introspection calls system.listMethods, system.methodSignature, + and system.methodHelp, + and the standard batching call system.multicall. With the + exception of these calls, all PLCAPI calls take an + authentication structure as their first argument. All + authentication structures require the specification of + AuthMethod. 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: + + + + Session authentication. User sessions are typically + valid for 24 hours. Node sessions are valid until the next + reboot. Obtain a session key with GetSession using another form of + authentication, such as password or GnuPG + authentication. + + + + AuthMethodsession + sessionSession key + + + + + + Password authentication. + + + + AuthMethodpassword + UsernameUsername, typically an e-mail address + AuthStringAuthentication string, typically a password + + + + + + GnuPG authentication. Users may upload a GPG public key + using AddPersonKey. Peer + GPG keys should be added with AddPeer or UpdatePeer. + + + + + AuthMethodgpg + namePeer or user name + signatureGnuPG signature of + the canonicalized + XML-RPC + representation of the rest of the arguments to the + call. + + + + + + Anonymous authentication. + + + + AuthMethodanonymous + + + + + +
+ +
+ Roles + + Some functions may only be called by users with certain + roles (see GetRoles), and others + may return different information to different callers depending + on the role(s) of the caller. + + The node and + anonymous roles are pseudo-roles. A function + that allows the node role may be called by + automated scripts running on a node, such as the Boot and Node + Managers. A function that allows the + anonymous role may be called by anyone; an + API authentication structure must still be specified (see ). +
+ +
+ Filters + + Most of the Get 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, + + +# plcsh code fragment (see below) +GetNodes([1,2,3]) +GetNodes({'node_id': [1,2,3]}) + + + Would be equivalent queries. Attributes that are + themselves arrays (such as nodenetwork_ids + and slice_ids for nodes) cannot be used in + filters. + + Filters support a few extra features illustrated in the following examples. + + + Pattern Matching + GetNodes ( { 'hostname' : '*.fr' } ) + + + Negation + GetNodes( { '~peer_id' : None } ) + + + Numeric comparisons + GetEvents( { '>time' : 1178531418 } ) + GetEvents( { ']event_id' : 2305 } ) + + + Sorting and Clipping + GetNodes( { '-SORT' : 'hostname' , '-OFFSET' : 30 , '-LIMIT' : 25 } + + +
+ +
+ PlanetLab shell + + A command-line program called plcsh + simplifies authentication structure handling, and is useful for + scripting. This program is distributed as a Linux RPM called + PLCAPI and requires Python ≥2.4. + + +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 + + + Specify at least the API URL and your user name: + + +plcsh --url https://www.planet-lab.org/PLCAPI/ -u user@site.edu + + + 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. + + +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'}] + + + 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. + + To use plcsh programmatically, import + the PLC.Shell module: + + +#!/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']) + +
+
+ + + PlanetLab API Methods + + + &Methods; + + +
+ + + + -- 2.47.0