-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathinfo-dynamic-condor
executable file
·224 lines (202 loc) · 8.74 KB
/
info-dynamic-condor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#!/usr/bin/env python
# coding: utf8
import htcondor
import argparse
import datetime
class InfoDynamicCondor(object):
"""
InfoDynamicCondor is a object to store usefull data
"""
def __init__(self, scheduler_address):
"""
Initialize InfoDynamicCondor
"""
self.name = "condor"
self.scheduler_address = scheduler_address
self.version = htcondor.version()[16:22]
self.cpus = 0
self.free = 0
self.vo = {}
# TODO: This value should be compute
self.max_waiting = 2000
self.max_memory = 2048
self.max_slot_per_job = 8
self.max_virtual_memory = 3072
self.max_wall_clock_time = 2160
self.max_cpu_time = 2160
self.status = 'Production'
def load(self):
"""
Retrieve information from HTCondor and populate object with it
"""
collector = htcondor.Collector()
# Collector provide information about worker node state
worker_nodes = collector.query(htcondor.AdTypes.Startd)
self.total_cpus(worker_nodes)
self.total_free(worker_nodes)
# Scheduler provide information about jobs
scheduler_address = collector.locate(htcondor.DaemonTypes.Schedd, self.scheduler_address)
scheduler = htcondor.Schedd(scheduler_address)
jobs = scheduler.query()
self.per_vo_jobs(jobs)
def total_cpus(self, worker_nodes):
"""
cpus is just a addition of Cpus find on all Worker Node
"""
for worker_node in worker_nodes:
self.cpus += worker_node['Cpus']
def total_free(self, worker_nodes):
"""
Each Cpus in worker node unclaimed is considered free
"""
for worker_node in worker_nodes:
if worker_node['State'] == "Unclaimed":
self.free += worker_node['Cpus']
def total_running(self):
"""
Running job is a addition of all job running for each vo
"""
running = 0
for vo in self.vo:
running += self.vo[vo]['running']
return running
def total_waiting(self):
"""
Waiting job is a addition of all job waiting for each vo
"""
waiting = 0
for vo in self.vo:
waiting += self.vo[vo]['waiting']
return waiting
def per_vo_jobs(self, jobs):
"""
Compute information about job for each VO
"""
for job in jobs:
# We only considere Running and Idle job, we ignore any other state (completed, ...)
if job['JobStatus'] == 1 or job['JobStatus'] == 2:
# If this is the first job for this VO we create a empty entry
if job['x509UserProxyVOName'] not in self.vo:
self.vo[job['x509UserProxyVOName']] = {'total': 0,
'running': 0,
'waiting': 0
}
# TODO: Is it really useful ?
self.vo[job['x509UserProxyVOName']]['total'] += 1
if job['JobStatus'] == 1:
self.vo[job['x509UserProxyVOName']]['waiting'] += 1
else:
self.vo[job['x509UserProxyVOName']]['running'] += 1
class ConsoleView(object):
"""
Class to display a human reading format of status
"""
@staticmethod
def display(dynamic_information):
information = dynamic_information.__dict__
information['waiting'] = dynamic_information.total_waiting()
information['running'] = dynamic_information.total_running()
print "######################\n" \
"# Global Information #\n" \
"######################\n" \
"Total CPUs: {cpus}\n" \
"Free CPUS: {free}\n" \
"Waiting Jobs: {waiting}\n" \
"Running Jobs: {running}\n" \
"".format(**information)
for vo in information['vo']:
vo_information = information['vo'][vo]
vo_information['name'] = vo
print " * {name}\n" \
" Waiting Jobs: {waiting}\n" \
" Running Jobs: {running}\n" \
"".format(**vo_information)
class Glue1View(object):
"""
Class to display into GLUE1 information
"""
@staticmethod
def display(dynamic_information, glue1_ldif):
glue_file = open(glue1_ldif, "r")
for line in glue_file:
if line.startswith('dn: GlueCEUniqueID='):
print line + "" \
"GlueCEInfoTotalCPUs: {cpus}\n" \
"GlueCEPolicyAssignedJobSlots: {cpus}\n" \
"GlueCEStateFreeCPUs: {free}\n" \
"GlueCEStateFreeJobSlots: {free}\n" \
"GlueCEPolicyMaxRunningJobs: {cpus}\n" \
"GlueCEPolicyMaxWallClockTime: {max_wall_clock_time}\n" \
"GlueCEPolicyMaxCPUTime: {max_cpu_time}\n" \
"GlueCEStateStatus: {status}\n" \
"GlueCEInfoLRMSVersion: {version}\n" \
"GlueCEInfoLRMSType: {name}".format(**dynamic_information.__dict__)
if line.startswith('dn: GlueVOViewLocalID='):
# TODO: Add a per-vo output (VOView)
pass
file.close()
class Glue2View(object):
"""
Class to display into GLUE2 information
"""
@staticmethod
def display(dynamic_information, glue2_ldif):
# TODO: Make a per-vo output instead of global output
information = dynamic_information.__dict__
information['waiting'] = dynamic_information.total_waiting()
information['max_total'] = dynamic_information.max_waiting + dynamic_information.cpus
information['current_date'] = datetime.datetime.now().isoformat()
glue_file = open(glue2_ldif, "r")
for line in glue_file:
if line.startswith('dn: GLUE2ShareID'):
print line + "" \
"GLUE2ComputingShareFreeSlots: {free}\n" \
"GLUE2ComputingShareMaxRunningJobs: {cpus}\n" \
"GLUE2ComputingShareMaxWaitingJobs: {max_waiting}\n" \
"GLUE2ComputingShareMaxTotalJobs: {max_total}\n" \
"GLUE2ComputingShareServingState: {status}\n" \
"GLUE2EntityCreationTime: {current_date}\n" \
"GLUE2ComputingShareMaxWallTime: {max_wall_clock_time}\n" \
"GLUE2ComputingShareDefaultWallTime: {max_wall_clock_time}\n" \
"GLUE2ComputingShareMaxCPUTime: {max_cpu_time}\n" \
"GLUE2ComputingShareDefaultCPUTime: {max_cpu_time}\n" \
"GLUE2ComputingShareMaxSlotsPerJob: {max_slot_per_job}\n" \
"GLUE2ComputingShareMaxMainMemory: {max_memory}\n" \
"GLUE2ComputingShareMaxVirtualMemory: {max_virtual_memory}" \
"".format(**information)
file.close()
def main():
"""
Main function for info-dynamic-condor
"""
###################
# Argument parser #
###################
parser = argparse.ArgumentParser(description="info-dynamic-condor return a GLUE1 / GLUE2 output"
"of condor scheduler state")
parser.add_argument('--scheduler', dest='scheduler',
help='Address of scheduler')
parser.add_argument('--glue1-ldif', dest='glue1_ldif',
help='Path to GLUE1 ldif file')
parser.add_argument('--glue2-ldif', dest='glue2_ldif',
help='Path to GLUE2 ldif file')
parser.add_argument('--console', dest='console', action='store_true',
help='Display Information w/ Human Reading output', default=False)
options = parser.parse_args()
##############################################
# Retrieve dynamic information from HTCondor #
##############################################
dynamic_information = InfoDynamicCondor(options.scheduler)
dynamic_information.load()
#######################
# Display information #
#######################
# print "ConsoleView"`
if options.console:
ConsoleView.display(dynamic_information)
if options.glue1_ldif is not None:
Glue1View.display(dynamic_information, options.glue1_ldif)
if options.glue2_ldif is not None:
Glue2View.display(dynamic_information, options.glue2_ldif)
if __name__ == '__main__':
main()