X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=doc%2FPLCAPI.xml.in;h=79fb1a8e83fbe32c016e24e9a9e95c618c5f18a0;hb=9aecb8c6fde06136bef94e2fd6fce832dc3c0c10;hp=ffc0057aa150e15d0907fd5ae3ce58cac831247c;hpb=c4b0a47c394d2196061b8c13a6cd57500accc866;p=plcapi.git diff --git a/doc/PLCAPI.xml.in b/doc/PLCAPI.xml.in index ffc0057..79fb1a8 100644 --- a/doc/PLCAPI.xml.in +++ b/doc/PLCAPI.xml.in @@ -1,4 +1,5 @@ + @@ -122,45 +123,328 @@
Filters - Most of the Get functions take a + 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, + attributes of the entities being queried. For example, -# plcsh code fragment (see below) -GetNodes([1,2,3]) -GetNodes({'node_id': [1,2,3]}) +>>> GetNodes([1,2,3]) +>>> GetNodes({'node_id': [1,2,3]}) + Would be equivalent queries. Attributes that are - themselves arrays (such as nodenetwork_ids + 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. + + - Pattern Matching - GetNodes ( { 'hostname' : '*.fr' } ) + + 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 :). + + - Negation - GetNodes( { '~peer_id' : None } ) + + 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. + + - Numeric comparisons - GetEvents( { '>time' : 1178531418 } ) - GetEvents( { ']event_id' : 2305 } ) + 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 + - Sorting and Clipping - GetNodes( { '-SORT' : 'hostname' , '-OFFSET' : 30 , '-LIMIT' : 25 } + 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 @@ -240,6 +524,48 @@ 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 +f=open('mynodes.txt','w') +for node in slice_nodes: + print >>f,node['hostname'] +f.close() + +
+