1 <?xml version="1.0" encoding="UTF-8"?>
3 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
5 <!ENTITY Methods SYSTEM "Methods.xml">
10 <title>PlanetLab Central API Documentation</title>
13 <chapter id="Introduction">
14 <title>Introduction</title>
16 <para>The PlanetLab Central API (PLCAPI) is the interface through
17 which the PlanetLab Central database should be accessed and
18 maintained. The API is used by the website, by nodes, by automated
19 scripts, and by users to access and update information about
20 users, nodes, sites, slices, and other entities maintained by the
23 <section id="Authentication">
24 <title>Authentication</title>
26 <para>The API should be accessed via XML-RPC over HTTPS. The API
27 supports the standard introspection calls <link
28 linkend="system.listMethods">system.listMethods</link>, <link
29 linkend="system.methodSignature">system.methodSignature</link>,
30 and <link linkend="system.methodHelp">system.methodHelp</link>,
31 and the standard batching call <link
32 linkend="system.multicall">system.multicall</link>. With the
33 exception of these calls, all PLCAPI calls take an
34 authentication structure as their first argument. All
35 authentication structures require the specification of
36 <parameter>AuthMethod</parameter>. If the documentation for a
37 call does not further specify the authentication structure, then
38 any of (but only) the following authentication structures may be
43 <para>Session authentication. User sessions are typically
44 valid for 24 hours. Node sessions are valid until the next
45 reboot. Obtain a session key with <link
46 linkend="GetSession">GetSession</link> using another form of
47 authentication, such as password or GnuPG
48 authentication.</para>
49 <informaltable frame="none" rules="rows">
52 <row><entry>AuthMethod</entry><entry><literal>session</literal></entry></row>
53 <row><entry>session</entry><entry>Session key</entry></row>
59 <para>Password authentication.</para>
60 <informaltable frame="none" rules="rows">
63 <row><entry>AuthMethod</entry><entry><literal>password</literal></entry></row>
64 <row><entry>Username</entry><entry>Username, typically an e-mail address</entry></row>
65 <row><entry>AuthString</entry><entry>Authentication string, typically a password</entry></row>
71 <para>GnuPG authentication. Users may upload a GPG public key
72 using <link linkend="AddPersonKey">AddPersonKey</link>. Peer
73 GPG keys should be added with <link
74 linkend="AddPeer">AddPeer</link> or <link
75 linkend="UpdatePeer">UpdatePeer</link>.
77 <informaltable frame="none" rules="rows">
80 <row><entry>AuthMethod</entry><entry><literal>gpg</literal></entry></row>
81 <row><entry>name</entry><entry>Peer or user name</entry></row>
82 <row><entry>signature</entry><entry>GnuPG signature of
84 url="http://www.w3.org/TR/xml-c14n">canonicalized</ulink>
85 <ulink url="http://www.xmlrpc.com/spec">XML-RPC</ulink>
86 representation of the rest of the arguments to the
93 <para>Anonymous authentication.</para>
94 <informaltable frame="none" rules="rows">
97 <row><entry>AuthMethod</entry><entry><literal>anonymous</literal></entry></row>
108 <para>Some functions may only be called by users with certain
109 roles (see <link linkend="GetRoles">GetRoles</link>), and others
110 may return different information to different callers depending
111 on the role(s) of the caller.</para>
113 <para>The <literal>node</literal> and
114 <literal>anonymous</literal> roles are pseudo-roles. A function
115 that allows the <literal>node</literal> role may be called by
116 automated scripts running on a node, such as the Boot and Node
117 Managers. A function that allows the
118 <literal>anonymous</literal> role may be called by anyone; an
119 API authentication structure must still be specified (see <xref
120 linkend="Authentication"/>).</para>
123 <section id="Filters">
124 <title>Filters</title>
126 <para>Most of the <function>Get</function> methods take a
127 filter argument. Filters may be arrays of integer (and sometimes
128 string) identifiers, or a struct representing a filter on the
129 attributes of the entities being queried. For example,
132 >>> GetNodes([1,2,3])
133 >>> GetNodes({'node_id': [1,2,3]})
137 <para>Would be equivalent queries. Attributes that are
138 themselves arrays (such as <literal>interface_ids</literal>
139 and <literal>slice_ids</literal> for nodes) cannot be used in
142 <para> Filters support a few extra features illustrated in the following examples.</para>
144 <section id="pattern-matching">
145 <title> Pattern Matching</title>
146 <para> <literal>*</literal> can be used in a text value and have the usual meaning, so all nodes in the <emphasis>fr</emphasis> can be obtained with:
147 <programlisting>GetNodes ( { 'hostname' : '*.fr' } ) </programlisting>
151 <section id="negation">
152 <title> Negation </title>
153 <para> Fields starting with a <literal>~</literal> are negated, so non-local nodes can be fetched with:
154 <programlisting>GetNodes( { '~peer_id' : None } ) </programlisting>
158 <section id="numeric">
159 <title> Numeric comparisons </title>
160 <para> Strictly greater/smaller operations are achieved by prepending the field name like in:
161 <programlisting>GetEvents( { '>time' : 1178531418 } ) </programlisting>
163 <para> Greater/smaller or equal:
164 <programlisting>GetEvents( { ']event_id' : 2305 } ) </programlisting>
168 <section id="sequence">
169 <title> Filtering on a sequence field </title>
170 <para> A field starting with '&' or '|' should refer to a sequence type;
171 the semantics is then that the object's value (expected to be a list)
172 should contain all (&) or any (|) value specified in the corresponding
174 <programlisting> GetPersons ( { '|role_ids' : [ 20, 40 ] } ) </programlisting>
175 <programlisting> GetPersons ( { '|roles' : ['tech', 'pi'] } ) </programlisting>
176 <programlisting> GetPersons ( { '&roles' : ['admin', 'tech'] } ) </programlisting>
177 <programlisting> GetPersons ( { '&roles' : 'tech' } ) </programlisting>
181 <section id="sort-clip">
182 <title> Sorting and Clipping </title>
183 <para> The following 3 special fields can be used to extract only a subset of the results for pagination:
184 <programlisting> GetNodes( { '-SORT' : 'hostname' , '-OFFSET' : 30 , '-LIMIT' : 25 }</programlisting>
189 <section id="and-or">
190 <title> All criteria / Any criteria </title>
191 <para> The default in the vast majority of the code is to select
192 objects that match ALL the criteria specified in the struct. It
193 is possible to search for objects that match ANY of these by
194 adding the special '-OR' key (the value is then ignored), as in:
195 <programlisting> GetPersons ( { '-OR' : 'anything', 'site_id':2, '&roles':['admin'] } ) </programlisting>
202 <para> The PLC API comes with a feature called
203 <emphasis>tags</emphasis>, that basically aims at supporting an
204 extensible data model. A few classes (as of this writing, Nodes,
205 Interfaces, Sites, Persons and Slices) are eligible for being dynamically
206 extended beyond the basic set of fields that are built into the
207 database schema.</para>
209 <para> Historically, this is a generalization of the concept of
210 <emphasis> SliceAttribute </emphasis>, and the more recent
211 concept of <emphasis> InterfaceSetting </emphasis>, that with
212 release 5.0 have been renamed into <emphasis> SliceTag
213 </emphasis> and <emphasis> InterfaceTag </emphasis>,
214 respectively. </para>
216 <section id="tags-low-level">
217 <title> Low level </title>
218 <para> The low level interface to tags relies on the following items:
221 A <emphasis> TagType </emphasis> object basically models a
222 new column that needs to be added to other objects. In much
223 the same way as nodes are named through a <emphasis>
224 hostname </emphasis>, tagtypes are named with a
225 <emphasis>tagname</emphasis>, plus additional information
226 (<emphasis>category</emphasis>,
227 <emphasis>description</emphasis>).
231 <emphasis>description</emphasis> is mostly informative, it
232 is used by the web interface to provide more details on
233 the meaning of that tag.
238 <emphasis>category</emphasis> is used in a variety of ways,
239 in the web interface again. Over time this has become a
240 means to attach various information to a tag type, so it is
241 used as some sort of a poorman's tag tag system :).
247 The convention is to set in category a set of slash-separated
248 fields, like the following real examples demonstrate.
250 >>> tagnames=['arch','fcdistro','hrn','hmac','exempt_node_until']
251 >>> for tt in GetTagTypes(tagnames,['tagname','category']):
252 >>> ... print "tagname=%-18s category=%s"%(tt['tagname'], tt['category'])
253 tagname=hrn category=node/sfa
254 tagname=hmac category=slice/auth
255 tagname=exempt_node_until category=node/myops
256 tagname=fcdistro category=node/slice/config/ui/header=f/rank=w
257 tagname=arch category=node/slice/config/ui/header=A/rank=x
262 <listitem> <para> <emphasis>roles</emphasis> may also be
263 attached to a given tag_type (use AddRoleToTagType or
264 DeleteRoleFromTagType). This is an evolution over the former
265 system based on so-called 'min_role_id', and now any set of
266 roles may be mentioned. More importantly, each type (Node,
267 Person, ...) implements its own policy to let or not non-admin
268 callers change their tags. For example in the current
269 implementation, non-admin users can only change their own
270 person tags. See PLC/AuthorizeHelpers.py for that code.
274 <para> The low-level method for managaing tags is then, once
275 the TagType is known to the system, to attach a value to,
276 say, a Node, by calling <emphasis> AddNodeTag </emphasis>,
277 and then as usual change this value with <emphasis>
278 UpdateNodeTag </emphasis>, or delete it with <emphasis>
279 DeleteNodeTag </emphasis>. </para>
285 <section id="accessors">
286 <title> Accessors </title>
287 <para> A rather more convenient way to use tags is through
288 Accessors. This convenience is located in <emphasis>
289 PLC/Accessors </emphasis>, and allows you to easily define Get
290 or Set methods dedicated to a given tag. This is for instance
291 how the <emphasis> GetNodeArch </emphasis> and <emphasis>
292 SetNodeArch </emphasis> methods are implemented. These methods
293 greatly simplify tags manipulation as they take care of
296 <para> Creating and enforcing <emphasis> TagTypes
297 </emphasis>; each time you restart your plc, the tag_types
298 mentioned in accessor definitions are created and checked
299 (in terms of the category, description and roles defined in
300 the various calls to define_accessors).</para>
303 <para> Create or update the, say, <emphasis> NodeTag
304 </emphasis> object, as needed.</para>
306 <listitem> <para> In addition, an accessor definition mentions
307 <emphasis> get_roles </emphasis> (defaults to all_roles), and
308 <emphasis>set_roles </emphasis>. These values are used as
309 follows. <emphasis> get_roles </emphasis> is attached to the
310 Get accessor, so callers that do not have this role cannot run
311 the Get accessor. <emphasis> set_roles </emphasis> is attached
312 to the Set accessor, as well as to the corresponding TagType,
313 which in turn is used for checking write access to the tag
319 <para> <emphasis> Site-specific </emphasis> accessors can be
320 defined in <emphasis>
321 /usr/share/plc_api/PLC/Accessors/Accessors_site.py </emphasis>
322 and will be preserved across updates of the
323 <emphasis>plcapi</emphasis> rpm.
327 The accessors mechanism does not currently support setting slice
328 tags that apply only on a given node or nodegroup.
332 <section id="expose-in-api">
333 <title> Through regular Add/Get/Update methods </title>
335 Finally, tags may also get manipulated through the
336 <emphasis>AddNode</emphasis>, <emphasis>GetNodes</emphasis>,
337 and <emphasis>UpdateNode</emphasis> methods:
341 The <literal>define_accessors</literal> function in the
342 Accessors factory has an optional argument named <literal>
343 expose_in_api </literal>. When this is set, the
344 corresponding tag becomes visible from the Add/Get/Update
345 methods almost as if it was a native tag.
349 So for instance the following code would be legal and do as expected:
351 # create a x86_64 node
352 >>> AddNode({'hostname':'pl1.foo.com','arch':'x86_64'})
353 # get details for pl1.foo.com including tag 'arch' tag
354 >>> GetNodes(['pl1.foo.com'],['boot_state','node_type','arch'])
355 # set the 'deployment' tag
356 >>> UpdateNode('pl1.foo.com',{'deployment':'beta'})
357 # get all alpha and beta nodes
358 >>> GetNodes({'deployment':'*a'},['hostname','deployment'])
363 The current limitation about tags as opposed to native
364 fields is that, for performance, tags won't get returned
365 when using the implicit set of columns. So for instance:
367 # get all details for 'pl1.foo.com'
368 >>> node=GetNodes(['pl1.foo.com'])[0]
369 # this did not return the 'arch' tag
380 <section id="nodegroups">
381 <title>Nodegroups</title>
383 <para> In earlier versions up to v4.2, <emphasis> NodeGroups
384 </emphasis> used to be defined extensively. So you would,
385 basically, create an empty nodegroup instance, and then use
386 <emphasis> AddNodeToNodeGroup </emphasis> or <emphasis>
387 DeleteNodeFromNodeGroup </emphasis> to manage the nodegroup's
390 <para> The new model has been redefined as follows. You now define
391 a nodegroup as the set of nodes for which a given <emphasis> Tag
392 </emphasis> has a given value, which are defined once and for good
393 when creating the <emphasis> NodeGroup </emphasis> object. </para>
395 <para> So for instance for managing the set of nodes that are
396 running various levels of software code, PLC has defined two
397 <emphasis> NodeGroups </emphasis> named <literal> alpha </literal>
398 and <literal> beta </literal>. With the new model, we would now do
399 something like the following, using the built-in <literal>
400 deployment </literal> tag that is created for that purpose:
402 ### creating node groups
403 >>> AddNodeGroup('alphanodes','deployment','alpha')
405 >>> AddNodeGroup('betanodes','deployment','beta')
407 ### checking contents (no node has 'deployment' set to either 'alpha' or 'beta' yet)
408 >>> for ng in GetNodeGroups(['alphanodes','betanodes'],['groupname','node_ids']): print ng
409 {'groupname': u'alphanodes', 'node_ids': []}
410 {'groupname': u'betanodes', 'node_ids': []}
412 ### displaying node ids
413 >>> for n in GetNodes({'hostname':'*.inria.fr'},['hostname','node_id']): print n
414 {'hostname': u'vnode01.inria.fr', 'node_id': 1}
415 {'hostname': u'vnode02.inria.fr', 'node_id': 2}
417 ### setting 'deployment' for these two nodes
418 >>> SetNodeDeployment('vnode01.inria.fr','alpha')
419 >>> for ng in GetNodeGroups(['alphanodes','betanodes'],['groupname','node_ids']): print ng
420 {'groupname': u'alphanodes', 'node_ids': [1]}
421 {'groupname': u'betanodes', 'node_ids': []}
422 >>> SetNodeDeployment('vnode02.inria.fr','beta')
424 ### checking contents again
425 >>> for ng in GetNodeGroups(['alphanodes','betanodes'],['groupname','node_ids']): print ng
426 {'groupname': u'alphanodes', 'node_ids': [1]}
427 {'groupname': u'betanodes', 'node_ids': [2]}
434 <title>PlanetLab shell</title>
436 <para>A command-line program called <command>plcsh</command>
437 simplifies authentication structure handling, and is useful for
438 scripting. This program is distributed as a Linux RPM called
439 PLCAPI and requires Python ≥2.4.</para>
442 usage: plcsh [options]
445 -f CONFIG, --config=CONFIG
446 PLC configuration file
447 -h URL, --url=URL API URL
448 -c CACERT, --cacert=CACERT
450 -k INSECURE, --insecure=INSECURE
451 Do not check SSL certificate
452 -m METHOD, --method=METHOD
453 API authentication method
454 -s SESSION, --session=SESSION
456 -u USER, --user=USER API user name
457 -p PASSWORD, --password=PASSWORD
459 -r ROLE, --role=ROLE API role
460 -x, --xmlrpc Use XML-RPC interface
461 --help show this help message and exit
464 <para>Specify at least the API URL and your user name:</para>
467 plcsh --url https://www.planet-lab.org/PLCAPI/ -u user@site.edu
470 <para>You will be presented with a prompt. From here, you can
471 invoke API calls and omit the authentication structure, as it will
472 be filled in automatically.</para>
475 user@site.edu connected using password authentication
476 Type "system.listMethods()" or "help(method)" for more information.
477 [user@site.edu]>>> AuthCheck()
479 [user@site.edu]>>> GetNodes([121], ['node_id', 'hostname'])
480 [{'node_id': 121, 'hostname': 'planetlab-1.cs.princeton.edu'}]
483 <para>As this program is actually a Python interpreter, you may
484 create variables, execute for loops, import other packages, etc.,
485 directly on the command line as you would using the regular Python
488 <para>To use <command>plcsh</command> programmatically, import
489 the <function>PLC.Shell</function> module:</para>
496 # Default location that the PLCAPI RPM installs the PLC class
497 sys.path.append('/usr/share/plc_api')
499 # Initialize shell environment. Shell() will define all PLCAPI methods
500 # in the specified namespace (specifying globals() will define them
502 from PLC.Shell import Shell
503 plc = Shell(globals(),
504 url = "https://www.planet-lab.org/PLCAPI/",
505 user = "user@site.edu",
506 password = "password")
508 # Both are equivalent
509 nodes = GetNodes([121], ['node_id', 'hostname'])
510 nodes = plc.GetNodes([121], ['node_id', 'hostname'])
514 <section id='standalone'>
515 <title>Using regular python</title>
517 <para>It is also possible to write simple regular-python scripts,
518 as illustrated in the example below. The only difference with the
519 examples above is that all API calls need to be passed a first
520 argument for authentication. This example would write in a file
521 the name of all the hosts attached to a given slice.</para>
524 #!/usr/bin/env python
528 plc_host='www.planet-lab.eu'
530 slice_name='inria_heartbeat'
532 auth = { 'AuthMethod' : 'password',
533 'Username' : 'thierry.parmentelat@inria.fr',
534 'AuthString' : 'xxxxxx',
537 api_url="https://%s:443/PLCAPI/"%plc_host
539 plc_api = xmlrpclib.ServerProxy(api_url,allow_none=True)
541 # the slice's node ids
542 node_ids = plc_api.GetSlices(auth,slice_name,['node_ids'])[0]['node_ids']
544 # get hostname for these nodes
545 slice_nodes = plc_api.GetNodes(auth,node_ids,['hostname'])
548 f=open('mynodes.txt','w')
549 for node in slice_nodes:
550 print >>f,node['hostname']
557 <chapter id="Methods">
558 <title>PlanetLab API Methods</title>
566 <!-- LocalWords: PlanetLab API PLCAPI RPC HTTPS listMethods methodSignature
568 <!-- LocalWords: methodHelp multicall AuthMethod GetSession GnuPG Username GPG
570 <!-- LocalWords: AuthString AddPersonKey AddPeer UpdatePeer gpg