1 |
ewv |
1.1 |
#!/usr/bin/env python
|
2 |
|
|
import re, sys
|
3 |
|
|
import ldap
|
4 |
|
|
|
5 |
|
|
DEBUG = 0
|
6 |
|
|
ce_to_cluster_map = {}
|
7 |
|
|
cluster_to_site_map = {}
|
8 |
|
|
|
9 |
|
|
def runldapquery(filter, attribute, bdii):
|
10 |
|
|
if DEBUG:
|
11 |
|
|
print "runldapquery ["+bdii+"]", filter[0:100], attribute[0:100]
|
12 |
|
|
attribute = attribute.split(' ')
|
13 |
|
|
filter = filter.strip()
|
14 |
|
|
filter = filter.lstrip("'").rstrip("'")
|
15 |
|
|
|
16 |
|
|
bdiiuri = 'ldap://' + bdii + ':2170'
|
17 |
|
|
l = ldap.initialize(bdiiuri)
|
18 |
|
|
|
19 |
|
|
l.simple_bind_s('', '')
|
20 |
|
|
|
21 |
|
|
base = "o=grid"
|
22 |
|
|
scope = ldap.SCOPE_SUBTREE
|
23 |
|
|
timeout = 0
|
24 |
|
|
result_set = []
|
25 |
|
|
filter = filter.strip("'")
|
26 |
|
|
|
27 |
|
|
try:
|
28 |
|
|
result_id = l.search(base, scope, filter, attribute)
|
29 |
|
|
while 1:
|
30 |
|
|
result_type, result_data = l.result(result_id, timeout)
|
31 |
|
|
if (result_data == []):
|
32 |
|
|
break
|
33 |
|
|
else:
|
34 |
|
|
if result_type == ldap.RES_SEARCH_ENTRY:
|
35 |
|
|
result_set.append(result_data)
|
36 |
|
|
|
37 |
|
|
except ldap.LDAPError, error_message:
|
38 |
|
|
print error_message
|
39 |
|
|
|
40 |
|
|
return result_set
|
41 |
|
|
|
42 |
|
|
def getJMListFromSEList(selist, bdii='exp-bdii.cern.ch'):
|
43 |
|
|
"""
|
44 |
|
|
Given a list of SE FQDNs, return list of CEUniqueIDs that advertise CMS
|
45 |
|
|
support and are in Production,
|
46 |
|
|
sorted by number of waiting jobs in descending order
|
47 |
|
|
"""
|
48 |
|
|
jmlist = []
|
49 |
|
|
|
50 |
|
|
query = buildOrQuery('GlueCESEBindGroupSEUniqueID', selist)
|
51 |
|
|
pout = runldapquery(query, 'GlueCESEBindGroupCEUniqueID', bdii)
|
52 |
|
|
|
53 |
|
|
query = "(&(GlueCEAccessControlBaseRule=VO:cms)(GlueCEStateStatus=Production)"
|
54 |
|
|
query += buildOrQuery('GlueCEUniqueID', [l[0][1]['GlueCESEBindGroupCEUniqueID'][0] for l in pout])
|
55 |
|
|
query += ")"
|
56 |
|
|
pout = runldapquery(query, 'GlueCEUniqueID GlueCEStateWaitingJobs', bdii)
|
57 |
|
|
|
58 |
|
|
jminfo_list = []
|
59 |
|
|
for x in pout:
|
60 |
|
|
jminfo = {}
|
61 |
|
|
ce = x[0][1]['GlueCEUniqueID'][0]
|
62 |
|
|
waiting_jobs = x[0][1]['GlueCEStateWaitingJobs'][0]
|
63 |
|
|
|
64 |
|
|
jminfo['ce'] = ce
|
65 |
|
|
jminfo['waiting_jobs'] = waiting_jobs
|
66 |
|
|
jminfo_list.append(jminfo)
|
67 |
|
|
|
68 |
|
|
def compare_by (fieldname):
|
69 |
|
|
""" Comparison function for sorting dicts """
|
70 |
|
|
def compare_two_dicts (a, b):
|
71 |
|
|
return cmp(int(a[fieldname]), int(b[fieldname]))
|
72 |
|
|
return compare_two_dicts
|
73 |
|
|
|
74 |
|
|
jminfo_list.sort(compare_by('waiting_jobs'))
|
75 |
|
|
jmlist = [x['ce'] for x in jminfo_list]
|
76 |
|
|
|
77 |
|
|
return jmlist
|
78 |
|
|
|
79 |
|
|
def generateMaps(ce_list, bdii='exp-bdii.cern.ch'):
|
80 |
|
|
"""
|
81 |
|
|
Generate maps of CE to Cluster and Cluster to Site as the globals
|
82 |
|
|
ce_to_cluster_map, cluster_to_site_map
|
83 |
|
|
|
84 |
|
|
ce_list: list of GlueCEUniqueIDs
|
85 |
|
|
bdii: BDII instance to query
|
86 |
|
|
"""
|
87 |
|
|
# if ce_to_cluster_map: return
|
88 |
|
|
query = buildOrQuery('GlueCEUniqueID', ce_list)
|
89 |
|
|
|
90 |
|
|
pout = runldapquery(query, 'GlueCEUniqueID GlueForeignKey', bdii)
|
91 |
|
|
|
92 |
|
|
r = re.compile('^GlueClusterUniqueID\s*=\s*(.*)')
|
93 |
|
|
for x in pout:
|
94 |
|
|
host = x[0][1]['GlueCEUniqueID'][0]
|
95 |
|
|
clusterid = x[0][1]['GlueForeignKey'][0]
|
96 |
|
|
m = r.match(clusterid)
|
97 |
|
|
if m: ce_to_cluster_map[host] = m.groups()[0]
|
98 |
|
|
|
99 |
|
|
query = "(&(objectClass=GlueCluster)"
|
100 |
|
|
query += buildOrQuery('GlueClusterUniqueID', ce_to_cluster_map.values())
|
101 |
|
|
query += ")"
|
102 |
|
|
|
103 |
|
|
pout = runldapquery(query, 'GlueClusterUniqueID GlueForeignKey', bdii)
|
104 |
|
|
r = re.compile('^GlueSiteUniqueID=(.*)')
|
105 |
|
|
for x in pout:
|
106 |
|
|
cluster = x[0][1]['GlueClusterUniqueID'][0]
|
107 |
|
|
foreign_keys = x[0][1]['GlueForeignKey']
|
108 |
|
|
for foreign_key in foreign_keys:
|
109 |
|
|
m = r.match(foreign_key)
|
110 |
|
|
if m:
|
111 |
|
|
site = m.groups()[0]
|
112 |
|
|
cluster_to_site_map[cluster] = site
|
113 |
|
|
|
114 |
|
|
def buildOrQuery(gluekey, list):
|
115 |
|
|
"""
|
116 |
|
|
Returns a nugget of LDAP requesting the OR of all items
|
117 |
|
|
of the list equal to the gluekey
|
118 |
|
|
"""
|
119 |
|
|
|
120 |
|
|
query = "(|"
|
121 |
|
|
for x in list:
|
122 |
|
|
query += "(%s=%s)" % (gluekey, x)
|
123 |
|
|
query += ")"
|
124 |
|
|
return query
|
125 |
|
|
|
126 |
|
|
def isOSGSite(host_list, bdii='exp-bdii.cern.ch'):
|
127 |
|
|
"""
|
128 |
|
|
Given a list of CEs, return only the ones which belong to OSG sites
|
129 |
|
|
"""
|
130 |
|
|
if (not ce_to_cluster_map): generateMaps(host_list, bdii)
|
131 |
|
|
|
132 |
|
|
query = buildOrQuery('GlueSiteUniqueID', cluster_to_site_map.values())
|
133 |
|
|
pout = runldapquery(query, 'GlueSiteUniqueID GlueSiteDescription', bdii)
|
134 |
|
|
osg_site_list = []
|
135 |
|
|
for x in pout:
|
136 |
|
|
site_descr = x[0][1]['GlueSiteDescription'][0]
|
137 |
|
|
|
138 |
|
|
if (site_descr.find('OSG') != -1):
|
139 |
|
|
osg_site_list.append(x[0][1]['GlueSiteUniqueID'][0])
|
140 |
|
|
|
141 |
|
|
osg_host_list = []
|
142 |
|
|
|
143 |
|
|
for host in host_list:
|
144 |
|
|
cluster = ce_to_cluster_map[host]
|
145 |
|
|
site = cluster_to_site_map[cluster]
|
146 |
|
|
|
147 |
|
|
if (osg_site_list.count(site)):
|
148 |
|
|
osg_host_list.append(host)
|
149 |
|
|
|
150 |
|
|
return osg_host_list
|
151 |
|
|
|
152 |
|
|
def getSoftwareAndArch(host_list, software, arch, bdii='exp-bdii.cern.ch'):
|
153 |
|
|
"""
|
154 |
|
|
Given a list of CEs, return only those that match a given software
|
155 |
|
|
and architecture tag
|
156 |
|
|
"""
|
157 |
|
|
if (not ce_to_cluster_map): generateMaps(host_list, bdii)
|
158 |
|
|
|
159 |
|
|
results_list = []
|
160 |
|
|
software = 'VO-cms-' + software
|
161 |
|
|
arch = 'VO-cms-' + arch
|
162 |
|
|
query = "'(&(GlueHostApplicationSoftwareRunTimeEnvironment="+software+ ")"
|
163 |
|
|
query += "(GlueHostApplicationSoftwareRunTimeEnvironment="+arch+")"
|
164 |
|
|
|
165 |
|
|
query += buildOrQuery('GlueChunkKey=GlueClusterUniqueID', [ce_to_cluster_map[h] for h in host_list])
|
166 |
|
|
query += ")"
|
167 |
|
|
|
168 |
|
|
pout = runldapquery(query, 'GlueHostApplicationSoftwareRunTimeEnvironment GlueChunkKey', bdii)
|
169 |
|
|
clusterlist = [x[0][1]['GlueChunkKey'][0] for x in pout]
|
170 |
|
|
|
171 |
|
|
results_list = []
|
172 |
|
|
for jm in host_list:
|
173 |
|
|
cluster = "GlueClusterUniqueID=" + ce_to_cluster_map[jm]
|
174 |
|
|
if (clusterlist.count(cluster) != 0):
|
175 |
|
|
results_list.append(jm)
|
176 |
|
|
|
177 |
|
|
return results_list
|
178 |
|
|
|
179 |
|
|
|
180 |
|
|
def getJobManagerList(selist, software, arch, bdii='exp-bdii.cern.ch', onlyOSG=True):
|
181 |
|
|
"""
|
182 |
|
|
Given a list of SE FQDNs, return list of CEUniqueIDs that advertise CMS
|
183 |
|
|
support and are in Production, sorted by number of waiting jobs in
|
184 |
|
|
descending order that have a given software and arch.
|
185 |
|
|
|
186 |
|
|
If OnlyOSG is True, return only OSG Sites.
|
187 |
|
|
"""
|
188 |
|
|
|
189 |
|
|
jmlist = getJMListFromSEList(selist, bdii)
|
190 |
|
|
generateMaps(jmlist, bdii)
|
191 |
|
|
if (onlyOSG): jmlist = isOSGSite(jmlist, bdii)
|
192 |
|
|
jmlist = getSoftwareAndArch(jmlist, software, arch, bdii)
|
193 |
|
|
res = removeQueues(jmlist)
|
194 |
|
|
return res
|
195 |
|
|
|
196 |
|
|
def removeQueues(celist):
|
197 |
|
|
"""
|
198 |
|
|
Given a list of CEUniqueIDs, return a list of jobmanager contact
|
199 |
|
|
strings.
|
200 |
|
|
"""
|
201 |
|
|
r = re.compile('^(.*:.*/jobmanager-.*?)-(.*)')
|
202 |
|
|
jmlist = []
|
203 |
|
|
for x in celist:
|
204 |
|
|
m = r.match(x)
|
205 |
|
|
if m:
|
206 |
|
|
item = m.groups()[0]
|
207 |
|
|
if (jmlist.count(item) == 0):
|
208 |
|
|
jmlist.append(item)
|
209 |
|
|
return jmlist
|
210 |
|
|
|
211 |
|
|
def listAllCEs(bdii='exp-bdii.cern.ch'):
|
212 |
|
|
''' List all GlueCEUniqueIDs that advertise support for CMS '''
|
213 |
|
|
|
214 |
|
|
RE_cename = re.compile('^GlueCEUniqueID: (.*)', re.IGNORECASE)
|
215 |
|
|
filt = "'(&(GlueCEUniqueID=*)(GlueCEAccessControlBaseRule=VO:cms))'"
|
216 |
|
|
res = runldapquery(filt, 'GlueCEUniqueID', bdii)
|
217 |
|
|
ceList = [x[0][1]['GlueCEUniqueID'][0] for x in res]
|
218 |
|
|
return ceList
|
219 |
|
|
|
220 |
|
|
def listAllSEs(bdii='exp-bdii.cern.ch'):
|
221 |
|
|
''' List all SEs that are bound to CEs that advertise support for CMS '''
|
222 |
|
|
|
223 |
|
|
RE_sename = re.compile('^GlueCESEBindGroupSEUniqueID: (.*)', re.IGNORECASE)
|
224 |
|
|
seList = []
|
225 |
|
|
ceList = listAllCEs(bdii)
|
226 |
|
|
|
227 |
|
|
query = buildOrQuery('GlueCESEBindGroupCEUniqueID', ceList)
|
228 |
|
|
res = runldapquery(query, 'GlueCESEBindGroupSEUniqueID', bdii)
|
229 |
|
|
|
230 |
|
|
for x in res:
|
231 |
|
|
try:
|
232 |
|
|
item = x[0][1]['GlueCESEBindGroupSEUniqueID'][0]
|
233 |
|
|
except KeyError:
|
234 |
|
|
# Sometimes we publish a CESE BindGroup without an SE attached
|
235 |
|
|
pass
|
236 |
|
|
|
237 |
|
|
if (seList.count(item) == 0): seList.append(item)
|
238 |
|
|
return seList
|