]> 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 methods 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, >>> GetNodes([1,2,3]) >>> GetNodes({'node_id': [1,2,3]}) Would be equivalent queries. Attributes that are themselves arrays (such as interface_ids and slice_ids for nodes) cannot be used in filters. Filters support a few extra features illustrated in the following examples.
Pattern Matching * can be used in a text value and have the usual meaning, so all nodes in the fr can be obtained with: GetNodes ( { 'hostname' : '*.fr' } )
Negation Fields starting with a ~ are negated, so non-local nodes can be fetched with: GetNodes( { '~peer_id' : None } )
Numeric comparisons Strictly greater/smaller operations are achieved by prepending the field name like in: GetEvents( { '>time' : 1178531418 } ) Greater/smaller or equal: GetEvents( { ']event_id' : 2305 } )
Filtering on a sequence field A field starting with '&' or '|' should refer to a sequence type; the semantics is then that the object's value (expected to be a list) should contain all (&) or any (|) value specified in the corresponding filter value. GetPersons ( { '|role_ids' : [ 20, 40 ] } ) GetPersons ( { '|roles' : ['tech', 'pi'] } ) GetPersons ( { '&roles' : ['admin', 'tech'] } ) GetPersons ( { '&roles' : 'tech' } )
Sorting and Clipping The following 3 special fields can be used to extract only a subset of the results for pagination: GetNodes( { '-SORT' : 'hostname' , '-OFFSET' : 30 , '-LIMIT' : 25 }
All criteria / Any criteria The default in the vast majority of the code is to select objects that match ALL the criteria specified in the struct. It is possible to search for objects that match ANY of these by adding the special '-OR' key (the value is then ignored), as in: GetPersons ( { '-OR' : 'anything', 'site_id':2, '&roles':['admin'] } )
Tags The PLC API comes with a feature called tags, that basically aims at supporting an extensible data model. A few classes (as of this writing, Nodes, Interfaces, Sites, Persons and Slices) are eligible for being dynamically extended beyond the basic set of fields that are built into the database schema. Historically, this is a generalization of the concept of SliceAttribute , and the more recent concept of InterfaceSetting , that with release 5.0 have been renamed into SliceTag and InterfaceTag , respectively.
Low level The low level interface to tags relies on the following items: A TagType object basically models a new column that needs to be added to other objects. In much the same way as nodes are named through a hostname , tagtypes are named with a tagname, plus additional information (category, description). description is mostly informative, it is used by the web interface to provide more details on the meaning of that tag. category is used in a variety of ways, in the web interface again. Over time this has become a means to attach various information to a tag type, so it is used as some sort of a poorman's tag tag system :). The convention is to set in category a set of slash-separated fields, like the following real examples demonstrate. >>> tagnames=['arch','fcdistro','hrn','hmac','exempt_node_until'] >>> for tt in GetTagTypes(tagnames,['tagname','category']): >>> ... print "tagname=%-18s category=%s"%(tt['tagname'], tt['category']) tagname=hrn category=node/sfa tagname=hmac category=slice/auth tagname=exempt_node_until category=node/myops tagname=fcdistro category=node/slice/config/ui/header=f/rank=w tagname=arch category=node/slice/config/ui/header=A/rank=x roles may also be attached to a given tag_type (use AddRoleToTagType or DeleteRoleFromTagType). This is an evolution over the former system based on so-called 'min_role_id', and now any set of roles may be mentioned. More importantly, each type (Node, Person, ...) implements its own policy to let or not non-admin callers change their tags. For example in the current implementation, non-admin users can only change their own person tags. See PLC/AuthorizeHelpers.py for that code. The low-level method for managaing tags is then, once the TagType is known to the system, to attach a value to, say, a Node, by calling AddNodeTag , and then as usual change this value with UpdateNodeTag , or delete it with DeleteNodeTag .
Accessors A rather more convenient way to use tags is through Accessors. This convenience is located in PLC/Accessors , and allows you to easily define Get or Set methods dedicated to a given tag. This is for instance how the GetNodeArch and SetNodeArch methods are implemented. These methods greatly simplify tags manipulation as they take care of Creating and enforcing TagTypes ; each time you restart your plc, the tag_types mentioned in accessor definitions are created and checked (in terms of the category, description and roles defined in the various calls to define_accessors). Create or update the, say, NodeTag object, as needed. In addition, an accessor definition mentions get_roles (defaults to all_roles), and set_roles . These values are used as follows. get_roles is attached to the Get accessor, so callers that do not have this role cannot run the Get accessor. set_roles is attached to the Set accessor, as well as to the corresponding TagType, which in turn is used for checking write access to the tag type. Site-specific accessors can be defined in /usr/share/plc_api/PLC/Accessors/Accessors_site.py and will be preserved across updates of the plcapi rpm. The accessors mechanism does not currently support setting slice tags that apply only on a given node or nodegroup.
Through regular Add/Get/Update methods Finally, tags may also get manipulated through the AddNode, GetNodes, and UpdateNode methods: The define_accessors function in the Accessors factory has an optional argument named expose_in_api . When this is set, the corresponding tag becomes visible from the Add/Get/Update methods almost as if it was a native tag. So for instance the following code would be legal and do as expected: # create a x86_64 node >>> AddNode({'hostname':'pl1.foo.com','arch':'x86_64'}) # get details for pl1.foo.com including tag 'arch' tag >>> GetNodes(['pl1.foo.com'],['boot_state','node_type','arch']) # set the 'deployment' tag >>> UpdateNode('pl1.foo.com',{'deployment':'beta'}) # get all alpha and beta nodes >>> GetNodes({'deployment':'*a'},['hostname','deployment']) The current limitations about tags, as opposed to native fields, is that for performance, tags won't get returned when using the implicit set of columns. So for instance: # get all details for 'pl1.foo.com' >>> node=GetNodes(['pl1.foo.com'])[0] # this did not return the 'arch' tag >>> 'arch' in node False For a similar reason, any tag used in the filter argument will have to be mentioned in the list of returned columns as well. For example: # if 'hrn' is not part of the result, this does not work >>> ns=GetNodes({'hrn':'ple.*'},['hostname']) Database error b59e068c-589a-4ad5-9dd8-63cc38f2a2eb: column "hrn" does not exist LINE 1: ...M view_nodes WHERE deleted IS False AND (True AND hrn ILIKE ... ... abridged ... # this can be worked around by just returning 'hrn' as well >>> ns=GetNodes({'hrn':'ple.*'},['hrn','hostname'])
Nodegroups In earlier versions up to v4.2, NodeGroups used to be defined extensively. So you would, basically, create an empty nodegroup instance, and then use AddNodeToNodeGroup or DeleteNodeFromNodeGroup to manage the nodegroup's contents. The new model has been redefined as follows. You now define a nodegroup as the set of nodes for which a given Tag has a given value, which are defined once and for good when creating the NodeGroup object. So for instance for managing the set of nodes that are running various levels of software code, PLC has defined two NodeGroups named alpha and beta . With the new model, we would now do something like the following, using the built-in deployment tag that is created for that purpose: ### creating node groups >>> AddNodeGroup('alphanodes','deployment','alpha') 21 >>> AddNodeGroup('betanodes','deployment','beta') 22 ### checking contents (no node has 'deployment' set to either 'alpha' or 'beta' yet) >>> for ng in GetNodeGroups(['alphanodes','betanodes'],['groupname','node_ids']): print ng {'groupname': u'alphanodes', 'node_ids': []} {'groupname': u'betanodes', 'node_ids': []} ### displaying node ids >>> for n in GetNodes({'hostname':'*.inria.fr'},['hostname','node_id']): print n {'hostname': u'vnode01.inria.fr', 'node_id': 1} {'hostname': u'vnode02.inria.fr', 'node_id': 2} ### setting 'deployment' for these two nodes >>> SetNodeDeployment('vnode01.inria.fr','alpha') >>> for ng in GetNodeGroups(['alphanodes','betanodes'],['groupname','node_ids']): print ng {'groupname': u'alphanodes', 'node_ids': [1]} {'groupname': u'betanodes', 'node_ids': []} >>> SetNodeDeployment('vnode02.inria.fr','beta') ### checking contents again >>> for ng in GetNodeGroups(['alphanodes','betanodes'],['groupname','node_ids']): print ng {'groupname': u'alphanodes', 'node_ids': [1]} {'groupname': u'betanodes', 'node_ids': [2]}
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'])
Using regular python It is also possible to write simple regular-python scripts, as illustrated in the example below. The only difference with the examples above is that all API calls need to be passed a first argument for authentication. This example would write in a file the name of all the hosts attached to a given slice. #!/usr/bin/env python import xmlrpclib plc_host='www.planet-lab.eu' slice_name='inria_heartbeat' auth = { 'AuthMethod' : 'password', 'Username' : 'thierry.parmentelat@inria.fr', 'AuthString' : 'xxxxxx', } api_url="https://%s:443/PLCAPI/"%plc_host plc_api = xmlrpclib.ServerProxy(api_url,allow_none=True) # the slice's node ids node_ids = plc_api.GetSlices(auth,slice_name,['node_ids'])[0]['node_ids'] # get hostname for these nodes slice_nodes = plc_api.GetNodes(auth,node_ids,['hostname']) # store in a file with ('mynodes.txt','a') as f: for node in slice_nodes: f.write(node['hostname'] + "\n") f.close()
PlanetLab API Methods &Methods;