Fix version output when missing.
[plcapi.git] / doc / PLCAPI.xml.in
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!-- -*-xml-*- -->
3 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
4 "@DOCBOOK-43@" [
5 <!ENTITY Methods SYSTEM "Methods.xml">
6 ]>
7
8 <book>
9   <bookinfo>
10     <title>PlanetLab Central API Documentation</title>
11   </bookinfo>
12
13   <chapter id="Introduction">
14     <title>Introduction</title>
15
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
21     database.</para>
22
23     <section id="Authentication">
24       <title>Authentication</title>
25
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
39       used:</para>
40
41       <itemizedlist>
42         <listitem>
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">
50             <tgroup cols="3">
51               <tbody>
52                 <row><entry>AuthMethod</entry><entry><literal>session</literal></entry></row>
53                 <row><entry>session</entry><entry>Session key</entry></row>
54               </tbody>
55             </tgroup>
56           </informaltable>
57         </listitem>
58         <listitem>
59           <para>Password authentication.</para>
60           <informaltable frame="none" rules="rows">
61             <tgroup cols="3">
62               <tbody>
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>
66               </tbody>
67             </tgroup>
68           </informaltable>
69         </listitem>
70         <listitem>
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>.
76           </para>
77           <informaltable frame="none" rules="rows">
78             <tgroup cols="3">
79               <tbody>
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
83                 the <ulink
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
87                 call.</entry></row>
88               </tbody>
89             </tgroup>
90           </informaltable>
91         </listitem>
92         <listitem>
93           <para>Anonymous authentication.</para>
94           <informaltable frame="none" rules="rows">
95             <tgroup cols="3">
96               <tbody>
97                 <row><entry>AuthMethod</entry><entry><literal>anonymous</literal></entry></row>
98               </tbody>
99             </tgroup>
100           </informaltable>
101         </listitem>
102       </itemizedlist>
103     </section>
104
105     <section id="Roles">
106       <title>Roles</title>
107
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>
112
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>
121     </section>
122
123     <section id="Filters">
124       <title>Filters</title>
125
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,
130
131 <programlisting>
132 >>> GetNodes([1,2,3])
133 >>> GetNodes({'node_id': [1,2,3]})
134 </programlisting>
135 </para>
136
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
140       filters.</para>
141
142       <para> Filters support a few extra features illustrated in the following examples.</para>
143       
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>
148         </para>
149       </section>
150
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>
155         </para>
156       </section>
157
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>
162         </para>
163         <para> Greater/smaller or equal: 
164         <programlisting>GetEvents( { ']event_id' : 2305 } ) </programlisting>
165         </para>
166       </section>
167
168       <section id="sort-clip">
169         <title> Sorting and Clipping </title> 
170         <para> The following 3 special fields can be used to extract only a subset of the results for pagination:
171           <programlisting> GetNodes( { '-SORT' : 'hostname' , '-OFFSET' : 30 , '-LIMIT' : 25 }</programlisting>
172         </para>
173       </section>
174     </section>
175
176     <section id="tags">
177       <title>Tags</title>
178
179       <para> The PLC API comes with a feature called
180       <emphasis>tags</emphasis>, that basically aims at supporting an
181       extensible data model. A few classes (as of this writing, Nodes,
182       Interfaces and Slices) are eligible for being dynamically
183       extended beyond the basic set of fields that are built into the
184       database schema.</para>
185
186       <para> Historically, this is a generalization of the concept of
187       <emphasis> SliceAttribute </emphasis>, and the more recent
188       concept of <emphasis> InterfaceSetting </emphasis>, that with
189       release 5.0 have been renamed into <emphasis> SliceTag
190       </emphasis> and <emphasis> InterfaceTag </emphasis>,
191       respectively. </para>
192
193       <section id="tags-low-level">
194         <title> Low level </title>
195         <para> The low level interface to tags relies on the following items:
196       <itemizedlist>
197         <listitem>
198           <para> 
199             A <emphasis> TagType </emphasis> object basically models a
200             new column that needs to be added to other objects. In
201             much the same way as nodes are named through a <emphasis>
202             hostname </emphasis>, tagtypes are named with a
203             <emphasis>tagname</emphasis>, plus additional information
204             (category, description) that is mostly informative. The
205             convention is to use a category that depicts the type of
206             objects that the tag type, like e.g. <emphasis>
207             node/config </emphasis> 
208           </para>
209         </listitem>
210         <listitem>
211           <para> You would then be allowed to attach a value to, say,
212           a Node, by calling <emphasis> AddNodeTag </emphasis>, and
213           then as usual change this value with <emphasis>
214           UpdateNodeTag </emphasis>, or delete it with <emphasis>
215           DeleteNodeTag </emphasis>. </para>
216         </listitem>
217       </itemizedlist>
218     </para>
219       </section>
220
221       <section id="accessors">
222         <title> Accessors </title>
223       <para> A rather more convenient way to use tags is through
224       Accessors. This convenience is located in <emphasis>
225       PLC/Accessors </emphasis>, and allows you to easily define Get
226       or Set methods dedicated to a given tag. This is for instance
227       how the <emphasis> GetNodeArch </emphasis> and <emphasis>
228       SetNodeArch </emphasis> methods are implemented. These methods
229       greatly simplify tags manipulation as they take care of
230       <itemizedlist>
231         <listitem>
232           <para> Lazily create <emphasis> TagTypes </emphasis> when
233           needed,</para>
234         </listitem>
235         <listitem>
236           <para> Create or update the, say, <emphasis> NodeTag
237           </emphasis> object, as needed.</para>
238         </listitem>
239       </itemizedlist>
240       </para>
241       <para> <emphasis> Site-specific </emphasis> accessors can be
242       defined in <emphasis>
243       /usr/share/plc_api/PLC/Accessors/Accessors_site.py </emphasis>
244       that will be preserved across updates of the PLCAPI rpm. 
245       </para>
246       <para> 
247         This mechanism does not currently support setting slice
248         tags that apply only on a given node or nodegroup. 
249       </para>
250       </section>
251
252       <section id="expose-in-api">
253         <title> Through regular Add/Get/Update methods </title>
254       <para> 
255         Finally, tags may also get manipulated through the
256         <emphasis>AddNode</emphasis>, <emphasis>GetNodes</emphasis>,
257         and <emphasis>UpdateNode</emphasis> methods:
258
259       <itemizedlist>
260         <listitem> <para> 
261           The <literal>define_accessors</literal> function in the
262           Accessors factory has an optional argument named <literal>
263           expose_in_api </literal>. When this is set, the
264           corresponding tag becomes visible from the Add/Get/Update
265           methods almost as if it was a native tag.
266         </para> </listitem>
267
268         <listitem><para>
269           So for instance the following code would be legal and do as expected:
270 <programlisting>
271 # create a x86_64 node
272 >>> AddNode({'hostname':'pl1.foo.com','arch':'x86_64'})
273 # get details for pl1.foo.com including tag 'arch' tag
274 >>> GetNodes(['pl1.foo.com'],['boot_state','node_type','arch'])
275 # set the 'deployment' tag
276 >>> UpdateNode('pl1.foo.com',{'deployment':'beta'})
277 # get all alpha and beta nodes
278 >>> GetNodes({'deployment':'*a'},['hostname','deployment'])
279 </programlisting>
280         </para></listitem>
281
282         <listitem><para> 
283           The current limitation about tags as opposed to native
284           fields is that, for performance, tags won't get returned
285           when using the implicit set of columns. So for instance:
286 <programlisting>
287 # get all details for 'pl1.foo.com' 
288 >>> node=GetNodes(['pl1.foo.com'])[0]
289 # this did not return the 'arch' tag
290 >>> 'arch' in node
291 False
292 </programlisting>
293         </para></listitem>
294
295       </itemizedlist>
296     </para>
297       </section>
298     </section>
299
300     <section id="nodegroups">
301     <title>Nodegroups</title>
302
303     <para> In earlier versions up to v4.2, <emphasis> NodeGroups
304     </emphasis> used to be defined extensively. So you would,
305     basically, create an empty nodegroup instance, and then use
306     <emphasis> AddNodeToNodeGroup </emphasis> or <emphasis>
307     DeleteNodeFromNodeGroup </emphasis> to manage the nodegroup's
308     contents. </para>
309
310     <para> The new model has been redefined as follows. You now define
311     a nodegroup as the set of nodes for which a given <emphasis> Tag
312     </emphasis> has a given value, which are defined once and for good
313     when creating the <emphasis> NodeGroup </emphasis> object. </para>
314
315     <para> So for instance for managing the set of nodes that are
316     running various levels of software code, PLC has defined two
317     <emphasis> NodeGroups </emphasis> named <literal> alpha </literal>
318     and <literal> beta </literal>. With the new model, we would now do
319     something like the following, using the built-in <literal>
320     deployment </literal> tag that is created for that purpose:
321 <programlisting>
322 >>> AddNodeGroup('alphanodes','deployment','alpha')
323 21
324 >>> AddNodeGroup('betanodes','deployment','beta')
325 21
326 >>> for ng in GetNodeGroups(['alphanodes','betanodes'],['groupname','node_ids']): print ng
327 {'groupname': u'alphanodes', 'node_ids': []}
328 {'groupname': u'betanodes', 'node_ids': []}
329 >>> SetNodeDeployment('vnode01.inria.fr','alpha')
330 >>> for ng in GetNodeGroups(['alphanodes','betanodes'],['groupname','node_ids']): print ng
331 {'groupname': u'alphanodes', 'node_ids': [1]}
332 {'groupname': u'betanodes', 'node_ids': []}
333 >>> SetNodeDeployment('vnode01.inria.fr','beta')
334 >>> for ng in GetNodeGroups(['alphanodes','betanodes'],['groupname','node_ids']): print ng
335 {'groupname': u'alphanodes', 'node_ids': []}
336 {'groupname': u'betanodes', 'node_ids': [1]}
337 </programlisting>
338 </para>  
339
340     </section>
341
342     <section id="plcsh">
343       <title>PlanetLab shell</title>
344
345       <para>A command-line program called <command>plcsh</command>
346       simplifies authentication structure handling, and is useful for
347       scripting. This program is distributed as a Linux RPM called
348       PLCAPI and requires Python &ge;2.4.</para>
349
350       <programlisting>
351 usage: plcsh [options]
352
353 options:
354   -f CONFIG, --config=CONFIG
355                         PLC configuration file
356   -h URL, --url=URL     API URL
357   -c CACERT, --cacert=CACERT
358                         API SSL certificate
359   -k INSECURE, --insecure=INSECURE
360                         Do not check SSL certificate
361   -m METHOD, --method=METHOD
362                         API authentication method
363   -s SESSION, --session=SESSION
364                         API session key
365   -u USER, --user=USER  API user name
366   -p PASSWORD, --password=PASSWORD
367                         API password
368   -r ROLE, --role=ROLE  API role
369   -x, --xmlrpc          Use XML-RPC interface
370   --help                show this help message and exit
371       </programlisting>
372
373       <para>Specify at least the API URL and your user name:</para>
374
375       <programlisting>
376 plcsh --url https://www.planet-lab.org/PLCAPI/ -u user@site.edu
377       </programlisting>
378
379       <para>You will be presented with a prompt. From here, you can
380       invoke API calls and omit the authentication structure, as it will
381       be filled in automatically.</para>
382
383       <programlisting>
384 user@site.edu connected using password authentication
385 Type "system.listMethods()" or "help(method)" for more information.
386 [user@site.edu]>>> AuthCheck()
387 1
388 [user@site.edu]>>> GetNodes([121], ['node_id', 'hostname'])
389 [{'node_id': 121, 'hostname': 'planetlab-1.cs.princeton.edu'}]
390       </programlisting>
391
392       <para>As this program is actually a Python interpreter, you may
393       create variables, execute for loops, import other packages, etc.,
394       directly on the command line as you would using the regular Python
395       shell.</para>
396
397       <para>To use <command>plcsh</command> programmatically, import
398       the <function>PLC.Shell</function> module:</para>
399
400       <programlisting>
401 #!/usr/bin/python
402
403 import sys
404
405 # Default location that the PLCAPI RPM installs the PLC class
406 sys.path.append('/usr/share/plc_api')
407
408 # Initialize shell environment. Shell() will define all PLCAPI methods
409 # in the specified namespace (specifying globals() will define them
410 # globally).
411 from PLC.Shell import Shell
412 plc = Shell(globals(),
413             url = "https://www.planet-lab.org/PLCAPI/",
414             user = "user@site.edu",
415             password = "password")
416
417 # Both are equivalent
418 nodes = GetNodes([121], ['node_id', 'hostname'])
419 nodes = plc.GetNodes([121], ['node_id', 'hostname'])
420       </programlisting>
421     </section>
422
423   <section id='standalone'>
424     <title>Using regular python</title>
425
426     <para>It is also possible to write simple regular-python scripts,
427     as illustrated in the example below. The only difference with the
428     examples above is that all API calls need to be passed a first
429     argument for authentication. This example would write in a file
430     the name of all the hosts attached to a given slice.</para>
431
432 <programlisting>
433 #!/usr/bin/env python
434
435 import xmlrpclib
436
437 plc_host='www.planet-lab.eu'
438 login='thierry.parmentelat@sophia.inria.fr'
439 password='xxxxxxxx'
440
441 slice_name='inria_heartbeat'
442
443 auth = { 'AuthMethod' : 'password',
444            'Username' : login,
445            'AuthString' : password,
446 }
447
448 api_url="https://%s:443/PLCAPI/"%plc_host
449
450 plc_api = xmlrpclib.ServerProxy(api_url,allow_none=True)
451
452 # the slice's node ids
453 node_ids = plc_api.GetSlices(auth,slice_name,['node_ids'])[0]['node_ids']
454
455 # get hostname for these nodes
456 slice_nodes = plc_api.GetNodes(auth,node_ids,['hostname'])
457
458 # store in a file
459 f=open('mynodes.txt','w')
460 for node in slice_nodes:
461     print >>f,node['hostname']
462 f.close()
463 </programlisting>
464   </section>
465
466   </chapter>
467
468   <chapter id="Methods">
469     <title>PlanetLab API Methods</title>
470     <para></para>
471
472     &Methods;
473   </chapter>
474
475 </book>
476
477 <!-- LocalWords:  PlanetLab API PLCAPI RPC HTTPS listMethods methodSignature
478 -->
479 <!-- LocalWords:  methodHelp multicall AuthMethod GetSession GnuPG Username GPG
480 -->
481 <!-- LocalWords:  AuthString AddPersonKey AddPeer UpdatePeer gpg
482 -->