|
|
![]() |
![]() |
![]() |
Table of Contents
This page explains how to use the ldapsearch command to perform complex queries of the EGEE information system.
It is assumed that the reader is familiar with the GLUE schema and the general structure of the information system. An introduction to this area can be found in the page Querying the Information System.
Useful references:
LDAP is a protocol through which information can be queried. An LDAP server supports this protocol and answers queries using locally stored data. LDAP is not described in detail here; see the references above for more details. However, in general terms information is organised in a set of objects, each with one or more objectclasses (types). Objectclasses generally represent a concept in a data model, similarly to tables in a relational database although the details are somewhat different. A specific object instance is then analogous to a row in a table.
Each objectclass is defined to have a set of named attributes, some of which may be optional, and which may be either single-valued (one such attribute per object) or multi-valued. Attribute names have a global scope, so different objectclasses cannot use the same attribute name for different things. Attributes also have types, typically string or integer althought they can be more complex. Strings can be defined as either case-sensitive or case-insensitive.
The objects are organised into a hierarchical tree, such that each
object is identified by a unique Distinguished Name (DN) of the form
attribute1=value1,attribute2=value2,attribute3=value3,...
with the root object name to the right and the leaf object to the left,
so that each object apart from the root is a child (or component)
of some other object.
This is known as the Directory Information Tree (DIT).
Each object has a specific attribute which is used to construct the DN.
The value of this attribute need not be unique, since objects with the same
value can be distinguished by their position in the tree.
Note that the DIT is constructed from actual objects, so the existence of an object requires the existence of its parent objects. For example, if an object has a DN of Name=Fred,Department=hr,Site=London then there must be objects with attribute values Department=hr and Site=London. However, the converse is not necessarily true.
The attributes and objectclasses, together with the way they are linked to form a DIT, are defined in a schema which is stored in the LDAP server. The server configuration also defines the port on which it listens for queries, and a DN to act as the root of the DIT.
In general LDAP is optimised to be able to serve large query volumes efficiently. However, for any query which is likely to be repeated frequently it may be worth giving some thought to minimising the number of queries, the number of objects searched by the query, and the amount of data returned.
The EGEE Grid uses an information schema known as the GLUE schema, currently at version 1.3. This is defined in an abstract way, and mappings exist for several different technologies including LDAP. The actual LDAP schema used in EGEE is referenced above, although the format is not especially easy to read. At present the schema used in EGEE defines all strings to be insensitive, although this may change in the future.
The DIT for the GLUE schema is relatively shallow, with the primary objects
(e.g. GlueCE
, GlueSE
, GlueService
,
GlueSite
) grouped below a single object for each site, and
a layer of subsidiary objects below them, e.g. GlueSA
below the
corresponding GlueSE
. For most purposes the DN of an object
is not very important as the schema defines UniqueID
attributes which are used to link objects together in a
technology-independent way. However, it cay be useful to use the DN to
restrict the scope of a query to a subtree if efficiency is important.
The LDAP mapping for GLUE defines two extra attributes to help with
queries. A GlueForeignKey
contains a reference to the
UniqueID
of a different object, e.g.
GlueForeignKey: GlueSEUniqueID=castorsrm.cern.ch
. Similarly
a GlueChunkKey
contains a reference to an object which is a
parent in the LDAP tree, e.g. a GluseSA
object might contain
GlueChunkKey: GlueSEUniqueID=castorsrm.cern.ch
.
The LDAP server in EGEE is known as a BDII (Berkely Database Information Index), which relates to the technology used to implement it. However, as far as queries go it is just a standard LDAP server. BDIIs are organised in a hierarchy, with so-called top-level BDIIs giving a view of the entire Grid, aggregating the content of site-level BDIIs which publish the data relevant to their site. These in turn aggregate the content of resource-level BDIIs running on each Grid service node, which collect information about the services on the node by running programs known as information providers.
The main command line tool to query an LDAP server is
ldapsearch
. The general structure of the command is:
ldapsearch -x -LLL -h <bdii-host> -p <bdii-port> -b mds-vo-name=local,o=grid \ [<filter>] [attribute1 [attribute2] ...]
or the equivalent:
ldapsearch -x -LLL -H ldap://<bdii-host>:<bdii-port>/ -b mds-vo-name=local,o=grid \ [<filter>] [attribute1 [attribute2] ...]
Going through the parameters in order:
-x
: turns off SASL authentication, which is not used with
the BDII.
-LLL
: removes some optional and largely useless elements in
the output, although specifying this option is not essential as the output
will usually be post-processed in any case. -L
and
-LL
can also be used for intermediate levels of output
reduction.
-h
: specifies the BDII host. Queries should generally be
made to a top-level BDII to get a view of the entire Grid, unless you
are only interested in a specific site. If you don't know a suitable
BDII you can use lcg-bdii.cern.ch
, or whatever is set in the
LCG_GFAL_INFOSYS
environment variable on your UI. Both
top-level and site-level BDIIs are published in the information system
itself as GlueService objects, so if you know one top-level BDII you can
find all the others.
-p
: specifies the port. This is normally 2170, although
older systems may still have a server on port 2135 if you query a CE or SE
directly.
-b
: specifies/restricts the branch of the LDAP DIT in
which the query is made (the query base). BDII trees are
rooted at o=grid
, and you can usually use just that for a
query to a top-level BDII. However, for compatibility with the old MDS
system the first node under that is called mds-vo-name=local
,
and it is somewhat safer to include that as well,
especially in scripts. At the site level there may be other branches under
o=grid
, e.g. mds-vo-name=<site-name>
or
mds-vo-name=resource
. You can also specify a query base
further down the tree, e.g. to restrict the query to a specific CE or SE.
The filter expression defines the query, and is described in more detail
below. It defaults to objectclass=*
, i.e. to query for all
objects. Note that you may need to use quotes with expressions
containing wildcards to avoid shell expansion. The query selects all objects
which match the filter expression.
The final part of the command is a list of attributes to return for the
selected objects; if this is omitted all attributes are returned. In
practice it is often more convenient to return everything and use a tool
like grep
to select the attributes to print.
One other option which is sometimes useful is -s one
, which
restricts the query to one level of objects below the base DN, rather than
searching the entire subtree.
There are various other options, but these are not usually needed for
Grid queries. See the ldapsearch
man page for full details.
The output from ldapsearch
is sent to stdout
as LDIF (LDAP Interchange Format),
see the LDIF man page or the RFC referenced above for details. The format is
reasonably readable, but not very flexible. In particular, lines longer than
78 characters are split, with the following line starting with a space. In
many cases it is necessary to post-process the output to make it more readable;
this can often be done with standard Unix pipeline tools like
grep
, awk
, cut
, sort
etc. One useful trick is that piping the LDIF through
perl -p00e 's/\r?\n //g'
will undo the line splitting mentioned
above (lines starting with a space are joined to the previous line).
For more complex applications it may be more useful to use an API
rather than a command-line tool, but these are not described here. For
the C API on which the command-line tool is based, see the man page for
ldap_search(3)
. There are also APIs available for other
languages, e.g. perl and python (see the references above). For an
example of use of the perl API, have a look at the source code for the
lcg-infosites
and lcg-info
tools, which should be
installed on a standard UI.
The filter expression used to define the query has a somewhat idiosyncratic syntax, but reasonably complex queries can be constructed with practice, although the flexibility is much less than with a language like SQL. The formal definition of the filter expression format can be found in RFC 2254, referenced above, but the following description covers the things which are generally needed in practice.
Basically the filter consists of a set of expressions
in parentheses which can be combined via & (AND), | (OR) and ! (NOT)
using a prefix notation, i.e. if A, B and C are valid expressions
then (& A B C)
, (| A B C)
and
(!A)
are also valid. The simplest expression is an
equality test, (a=b)
, where a
is an attribute
name and b
is a value.
For example, (&(a=b)(c=d))
means that attributes a
and c
must
have values b
and d
respectively to give a match.
Similarly, (|(!(a=b))(c=d))
matches if
attribute c
has the value d
or attribute
a
does not have the value b
. It is
not always obvious when parentheses are needed; in general it's best to
use them everywhere.
Objectclasses can be matched using an expression like
(objectclass=GlueCE)
. Components of DNs can be matched with
an expression like (GlueSEUniqueID:dn:=castorsrm.cern.ch)
-
in other words, this matches any object with a DN containing
GlueSEUniqueID=castorsrm.cern.ch
, as opposed to
(GlueSEUniqueID=castorsrm.cern.ch)
which matches an object
containing that attribute value. The *
character can
be used as a generic wildcard - but not in a query on a DN component.
Numeric comparisons can use >= and <= (only, i.e. there is no
< or >).
It is not possible to do relational queries, for example selecting the
GlueSE
objects with UniqueID
s which are referred
to in those GlueSA
objects which have a
GlueSAAccessControlBaseRule
attribute matching a given VO. In such
cases you have to do multiple queries.
The following examples give an idea of what can be achieved, building from simple cases to more complex ones. Usually the best way to develop something is to build the query up gradually so you can check that each stage does the right thing. The more complex examples are somewhat baroque and might be better done using an API from a programming language, but they do illustrate what can be achieved just with pipelines on the command line.
Note that the examples are not necessarily the best way of achieving a given result, they are just used to illustrate various strategies. For one-off queries it may well not matter exactly how you do it as long as you get the information you want, but for queries which will be done regularly it may be worth taking some time to find a way which is reasonably efficient and robust.
This is an elementary query. It would work without any filter as the
attribute name only appears in the GlueCE object, but would be somewhat
less efficient. Similarly the attribute name could be omitted as it gets
selected anyway with grep - this is done to remove DNs and blank lines from
the LDIF output. An extra step could be to use cut -d: -f2-
to strip off the attribute name.
ldapsearch -x -LLL -H ldap://lcg-bdii.cern.ch:2170/ -b mds-vo-name=local,o=grid \ objectclass=GlueCE GlueCEUniqueID | grep GlueCEUniqueID:
[...] GlueCEUniqueID: gridce.iihe.ac.be:2119/jobmanager-pbs-betest GlueCEUniqueID: lcg002.ihep.ac.cn:2119/jobmanager-lcgpbs-euchina GlueCEUniqueID: ce114.cern.ch:2119/jobmanager-lcglsf-grid_2nd_lhcb GlueCEUniqueID: polgrid1.in2p3.fr:2119/jobmanager-pbs-grif GlueCEUniqueID: ce103.cern.ch:2119/jobmanager-lcglsf-grid_geant4 GlueCEUniqueID: gridgate.cs.tcd.ie:2119/jobmanager-pbs-solovo [...]
The query here is slightly more complex, in that it uses a wildcard
filter to select only CEs with hostnames in UK domains. There is also
a bit more post-processing to present the result in a useful form. This also
illustrates the lack of case-sensitivity in the filter. However, remember
that grep
is case-sensitive by default.
ldapsearch -x -LLL -H ldap://lcg-bdii.cern.ch:2170/ -b mds-vo-name=local,o=grid \ '(&(objectclass=gluece)(glueceinfohostname=*.ac.uk))' \ glueceinfojobmanager | grep GlueCEInfoJobManager: \ | cut -d: -f2 | sort | uniq -c
11 lcgcondor 313 lcgpbs 3 lcgsge 21 pbs 16 sge
This uses the fact that the GlueSA
object, which contains the
VO assignment information, has an attribute GlueChunkKey
to
provide a link to the parent SE. There could potentially be several matching
SAs per SE, but the "sort | uniq
" ensures that only one is
counted. The access control format is expanding, but this gets all possible
variants currently in use.
For variety, this query uses a different BDII, the alternative format to
specify the host and port, and queries from o=grid
. It also doesn't
explicitly select an attribute name or use the -LLL
option
as the output gets filtered by the grep
anyway. The objectclass is
omitted as the attribute filter implies it.
ldapsearch -x -h lcgbdii02.gridpp.rl.ac.uk -p 2170 -b o=grid \ '(|(GlueSAAccessControlBaseRule=atlas)(GlueSAAccessControlBaseRule=VO:atlas)(GlueSAAccessControlBaseRule=VOMS:/atlas/*))' \ | grep 'GlueChunkKey: GlueSEUniqueID=' | sort | uniq | wc -l
218
This kind of query is used by the WMS to collect all information about sites
to be used in matchmaking, and illustrates a more complex use of AND and OR
conditions. The wc -c
just counts the number of bytes.
ldapsearch -x -h lcgbdii02.gridpp.rl.ac.uk -p 2170 -b o=grid \ '(|(objectclass=GlueSubCluster)(&(|(objectclass=GlueVOView)(objectclass=GlueCE)) \ (|(GlueCEAccessControlBaseRule=VO:cms)(GlueCEAccessControlBaseRule=VOMS:/cms/*))))' \ | wc -c
2523721
This example illustrates a double query: first find the
GlueSEAccessProtocol
objects which match a condition, extract
the GlueChunkKey
which contains a reference to the parent
GlueSE
object, and then query that for the
GlueForeignKey
which contains the site name. The post-processing
here just strips out the attribute name and removes any duplication.
One wrinkle is the use of a wildcard to match the GlueSEUniqueID. In general this is not needed. However, if the value is long enough it will get wrapped onto another line so the value extracted may be truncated; the wildcard ensures that it will still match, although in a sufficiently pathological case it might match other SEs too.
Efficiency is more of an issue here because the second query is executed
multiple times (once per matching SE). Adding an explicit restriction
objectclass=GlueSE
reduces the time for the query by around
a factor 2, from 20 seconds to 10.
for i in `ldapsearch -x -H ldap://lcg-bdii.cern.ch:2170 -b o=grid \ GlueSEAccessProtocolType=gsidcap | grep GlueChunkKey: | cut -d= -f2 \ | sort | uniq`; do ldapsearch -x -H ldap://lcg-bdii.cern.ch:2170 \ -b o=grid "GlueSEUniqueID=$i*" GlueForeignKey; done \ | grep GlueSiteUniqueID | cut -d= -f2 | sort | uniq
ALBERTA-LCG2 BEgrid-ULB-VUB BEIJING-LCG2 BG01-IPP BG04-ACAD BNL-LCG2 CIEMAT-LCG2 DESY-HH [...]
Here is another way to get the same result, using perl to join any split lines and sed to process the DN and use the result in the second query. This is somewhat more efficient (it takes about 7 seconds), but harder to write.
for dn in `ldapsearch -h lcg-bdii.cern.ch -p 2170 -x -b o=grid \ GlueSEAccessProtocolType=gsidcap | perl -p00e 's/\r?\n //g' \ | sed -ne 's/^dn: [^,]*,[^,]*,\(.*\)/\1/p' | sort -u`; do \ ldapsearch -h lcg-bdii.cern.ch -p 2170 -x -b $dn \ '(objectClass=GlueSite)' GlueSiteUniqueID \ | perl -p00e 's/\r?\n //g' | sed -ne 's/^GlueSiteUniqueID: \(.*\)/\1/p'; done
ALBERTA-LCG2 BEgrid-ULB-VUB BEIJING-LCG2 BG01-IPP BG04-ACAD BNL-LCG2 CIEMAT-LCG2 CSC CSCS-LCG2 DESY-HH [...]
This example first queries for the AccessControlBaseRule
entry, basically the VO name, for all CEs with a UK domain name, and
processes this to get a list of all supported VOs. One point to note is that
this does not cope with the new VOMS FQAN format for ACBRs as it stands,
but in practice it's unlikely that any CEs support only a subset of a VO.
There is then an outer loop over the VO list, which prints the VO name and
then queries for the CE hostnames, and sorts and prints them.
This is not especially elegant or efficient because it uses multiple queries simply to format the output. For serious use it would be better to use an API, store the output of a single query and format it programmatically. However, for ad-hoc queries this sort of thing gives a useful result with relatively little effort.
As an exercise for the reader, consider how to invert this to print a list of supported VOs for each CE.
for VO in `ldapsearch -x -h lcgbdii02.gridpp.rl.ac.uk -p 2170 -b o=grid \ "(&(objectclass=GlueCE)(GlueCEUniqueID=*.ac.uk*))" \ | grep "GlueCEAccessControlBaseRule: VO:" | sort | uniq | cut -d: -f3` ; \ do echo; echo $VO:; echo; \ ldapsearch -x -h lcgbdii02.gridpp.rl.ac.uk -p 2170 -b o=grid \ "(&(GlueCEUniqueID=*.ac.uk*)(GlueCEAccessControlBaseRule=VO:$VO))" \ GlueCEInfoHostName | grep GlueCEInfoHostName: | sort | uniq \ | cut -d: -f2; done
[...] esr: ce.epcc.ed.ac.uk fal-pygrid-18.lancs.ac.uk helmsley.dur.scotgrid.ac.uk hepgrid2.ph.liv.ac.uk heplnx206.pp.rl.ac.uk heplnx207.pp.rl.ac.uk lcgce02.gridpp.rl.ac.uk t2ce02.physics.ox.ac.uk t2ce03.physics.ox.ac.uk euindia: serv03.hep.phy.cam.ac.uk fusion: ce.epcc.ed.ac.uk fal-pygrid-18.lancs.ac.uk hepgrid2.ph.liv.ac.uk heplnx206.pp.rl.ac.uk heplnx207.pp.rl.ac.uk lcgce02.gridpp.rl.ac.uk t2ce02.physics.ox.ac.uk t2ce03.physics.ox.ac.uk [...]
This is getting to the point of being too complex for a command-line query,
but does illustrate what can be done if you really try. The problem here is
that the OS information is in the GlueSubCluster
object, which
is linked to a GlueCluster
object, which is linked to
GlueCE
objects which contain the CPU counts, so you need to do
at least two queries. The first one returns the GlueChunkKey
(containing the cluster ID) for all subclusters supporting SL4,
which itself requires a condition on two different attributes (with some
allowance for varying formats in the way the values are published).
The Cluster IDs are then carved out with cut
and piped into
another query with xargs
, to find GlueCE
objects
with those IDs in their GlueForeignKey
attributes. The query
returns the hostname and CPU count, which are then processed to remove the
attribute names and join them onto the same line.
The final "sort | uniq
" tries to allow for the fact that CEs
(queues) publishing the same number of CPUs are probably feeding jobs to the same
underlying set of nodes. This is not totally robust, but this is a feature
of the schema and information providers and can't be fixed at the query
level.
ldapsearch -x -h lcg-bdii.cern.ch -p 2170 -b o=grid \ '(&(objectclass=gluesubcluster)(gluehostoperatingsystemname=scien*)(gluehostoperatingsystemrelease=4*))' \ | grep GlueChunkKey: | cut -d= -f2 | xargs -n1 -i \ ldapsearch -x -h lcg-bdii.cern.ch -p 2170 -b o=grid \ '(&(objectclass=gluece)(glueforeignkey=glueclusteruniqueid={}))' \ GlueCEInfoHostName GlueCEInfoTotalCPUs | grep -A 1 GlueCEInfoHostName: \ | grep : | cut -d: -f 2 | xargs -l2 echo | sort | uniq
[...] ce.grid.hepg.sdu.edu.cn 8 ce.grid.tuke.sk 16 ce.grid.vgtu.lt 20 ce-iep-grid.saske.sk 57 ceitep.itep.ru 66 ce.phy.bg.ac.yu 49 ce.reef.man.poznan.pl 352 ce-test.pic.es 4 ce.ui.savba.sk 41 ce.ulakbim.gov.tr 65 cluster50.knu.ac.kr 276 cluster.pnpi.nw.ru 184 [...]