import repository from arizona
[raven.git] / apps / mprepo / grm.py
1 # /usr/bin/env python
2
3 import sys
4 sys.path = ["/usr/local/grm/bin"] + sys.path
5
6 from geni.util.cert import *
7 from geni.util.gid import *
8 from geni.util.credential import *
9
10 import arizonaconfig
11 import arizonageneral
12 import arizonareport
13 import arizonatemplate
14
15 import grmfrontend
16 import repoplc
17
18 import os
19 import repoconfig
20 import shutil
21 import tempfile
22 import time
23 import calendar
24 import gackslog_mysql
25
26 from html import *
27 from repoauth import *
28
29 from gackshandle import *
30 from gacksjob import *
31
32 from ravenlib.modpythonapi.BaseClient import EnableVerboseExceptions, EnableVerboseExceptionsTempFile
33 EnableVerboseExceptions(True)
34 EnableVerboseExceptionsTempFile(True)
35
36 ROOT = "/canopus/"
37
38 # directory where the gacks log may be found
39 LOG_DIR = "/usr/local/gackscentral/var/log"
40
41 class GrmError(Exception):
42     def __init__(self, value):
43         self.value = value
44     def __str__(self):
45         return repr(self.value)
46
47 def int_default(x, def_val=0):
48     try:
49         x = int(x)
50     except ValueError:
51         x = def_val;
52     return x
53
54 def report_error(helper, statusonly, e):
55     if statusonly:
56         helper.req.write("Error: " + str(e.value))
57         helper.done()
58     else:
59         helper.req.write( arizonatemplate.retrieve_template("errorpage.stemp",
60                                                             {"errormessage":str(e.value)}) )
61         helper.done()
62
63 def create_id_select(id=None, onChange=None):
64     options = ""
65
66     for name in ["cpu", "network", "disk", "cores"]:
67         selected = (name == str(id))
68         options += OPTION(name, selected, name)
69
70     return SELECT("id", options, id="id", onChange=onChange)
71
72 def time_to_str(helper, t):
73     if not t:
74         return "None"
75     if t==INFINITY:
76         return "Infinity"
77     t = t + helper.get_timezone() * 60 *60
78     gmt = time.gmtime(int(t))
79     return "%04d-%02d-%02d %02d:%02d:%02d" % gmt[0:6]
80     #return str(gmt[0]) + "-" + str(gmt[1]) + "-" + str(gmt[2]) + " " + str(gmt[3]) + ":" + str(gmt[4]) + ":" + str(gmt[5])
81
82 def create_delete_link(rec, allocator):
83     link = "delete?id=" + str(rec.id) + "&timeStart=" + str(rec.timeStart) + "&timeStop=" + str(rec.timeStop)
84     if allocator:
85         link += "&allocator=" + allocator
86     return LINK(link, "delete")
87
88 def create_asap_delete_link(rec):
89     link = "delete_asap?jobid=" + str(rec.get_jobid())
90     return LINK(link, "delete")
91
92 def create_edit_link(rec, allocator):
93     link = "edit?id=" + str(rec.id) + "&timeStart=" + str(rec.timeStart) + "&timeStop=" + str(rec.timeStop)
94     if allocator:
95         link += "&allocator=" + allocator
96     return LINK(link, "edit")
97
98 def ymd_to_time(helper, y=None, m=None, d=None, hour=None):
99     if (y==None) or (m==None) or (d==None):
100         gmt = time.gmtime(time.time() + int(helper.get_timezone()*60*60))
101         y=gmt[0]
102         m=gmt[1]
103         d=gmt[2]
104
105     y = int(y)
106     m = int(m)
107     d = int(d)
108
109     if hour:
110         h = int(hour)
111     else:
112         h = 0
113
114     return calendar.timegm((y, m, d, h, 0, 0, 0, 0, 0))
115
116 def create_asap_table(helper, ids=["cpu","disk","network"]):
117     table = TR(TH("resource") + TH("quantity"));
118
119     for id in ids:
120        row = TR(TD(id) + TD(INPUT(id)))
121        table = table + row
122
123     return TABLE(table)
124
125 def create_hour_table(helper, id, y=None, m=None, d=None, hour=None):
126     tStart = ymd_to_time(helper, y, m, d) - int(helper.get_timezone()*60*60)
127
128     recList = helper.get_gacks_client().query_overlap(id, timeStart=tStart, timeStop=tStart+60*60*24, isLastAllocator = helper.get_grm_gid().get_hrn())
129     table = TR(TH("") + TH("slot") + TH("avail") + TH("") + TH("") + TH("slot") + TH("avail"))
130     for i in range(0,12):
131         row = ""
132
133         tStartSlot = tStart + i*60*60
134         tStopSlot = tStartSlot + 60*60;
135         qty = count_records_time(recList, tStartSlot, tStartSlot+60)
136         selected = (str(hour) == str(i))
137         if (tStopSlot < time.time()):
138             row = row + TD("") + TD(str(i) + ":00:00") + TD(str(qty))
139         else:
140             row = row + TD(RADIO("hour", str(i), checked=selected)) + TD(str(i) + ":00:00") + TD(str(qty))
141
142         row = row + TD("&nbsp;&nbsp;&nbsp;")
143
144         tStartSlot = tStart + (i+12) * 60*60
145         tStopSlot = tStartSlot + 60*60;
146         selected = (str(hour) == str(i+12))
147         qty = count_records_time(recList, tStartSlot, tStartSlot+60)
148         if (tStopSlot < time.time()):
149             row = row + TD("") + TD(str(i+12) + ":00:00") + TD(str(qty))
150         else:
151             row = row + TD(RADIO("hour", str(i+12), checked=selected)) + TD(str(i+12) + ":00:00") + TD(str(qty))
152
153         table = table + TR(row)
154     table = TABLE(table)
155
156     return table
157
158 def create_date_table(helper, y=None, m=None, d=None, onChange=None):
159     if (y==None) or (m==None) or (d==None):
160         gmt = time.gmtime(time.time())
161         y=gmt[0]
162         m=gmt[1]
163         d=gmt[2]
164
165     return "year: " + INPUT("y", value=y, size=4, onChange=onChange) + " mon: " + INPUT("m", value=m, size=2, onChange=onChange) + " day: " + INPUT("d", value=d, size=2, onChange=onChange) #+ " " + BUTTON("submit_date", "update")
166
167 def create_handle_table(helper, allocator):
168     recList = helper.get_gacks_client().query_exact(hasAllocator = allocator)
169     if (recList == []):
170         return "No Canopus jobs in queue"
171
172
173     table = TR(TH("") + TH("id") + TH("start") + TH("stop") + TH("allocators") + TH("consumer") + TH("qty") + TH("rh"))
174     aggList = aggregate_records(recList)
175     keyList = aggList.keys()
176     for key in keyList:
177         rec = aggList[key][0]
178         qty = count_records(aggList[key])
179         rh = count_resource_hours(aggList[key])
180         if (rh==INFINITY):
181             rh="Infinity";
182         table = table + TR( TD(create_delete_link(rec, allocator) + " " + create_edit_link(rec,allocator)) + \
183                             TD(rec.id) + TD(time_to_str(helper, rec.timeStart)) + TD(time_to_str(helper, rec.timeStop)) + TD(", ".join(rec.allocatorHRNs)) + TD(rec.consumerHRN) + TD(qty) + TD(rh))
184     table = TABLE(table)
185     return table
186
187 def create_asap_job_table(helper, allocator):
188     recList = helper.get_gacks_client().query_asap(allocatorHRN=allocator)
189     if (recList == []):
190         return "No ASAP jobs in queue"
191
192     table = TR(TH("") + TH("id") + TH("allocator") + TH("consumer") + TH("duration") + TH("resources"))
193     for rec in recList:
194         table = table + TR( TD(create_asap_delete_link(rec)) + TD(rec.get_jobid()) + TD(rec.get_allocator()) + TD(rec.get_consumer()) + TD(str(rec.get_duration())) + TD(rec.get_resources_string()) )
195
196     table = TABLE(table)
197     return table
198
199 def create_detail_table(helper, id, timeStart=0, timeStop=INFINITY, allocator=None):
200     table = ""
201
202     recList = helper.get_gacks_client().query_exact(id = id, timeStart=timeStart, timeStop=timeStop, hasAllocator = allocator)
203
204     if not recList:
205         return "no handles match query"
206
207     handleStrs = []
208     unitStrs = []
209     for rec in recList:
210         handleStrs.append(rec.as_string())
211         unitStrs.append(str(rec.unitStart) + "-" + str(rec.unitStop))
212
213     rec0 = recList[0]
214
215     qty = count_records(recList)
216
217     table += TR( TD("resource") + TD(rec0.id) )
218     table += TR( TD("time start") + TD(time_to_str(helper, rec0.timeStart)) )
219     table += TR( TD("time stop") + TD(time_to_str(helper, rec0.timeStop)) )
220     table += TR( TD("quantity") + TD(str(qty) + " (" + ", ".join(unitStrs)+")") )
221     table += TR( TD("allocators") + TD(", ".join(rec0.allocatorHRNs)) )
222     table += TR( TD("consumer") + TD(rec0.consumerHRN) )
223     table += TR( TD("handles") + TD("\n".join(handleStrs)) )
224
225     table = TABLE(table)
226     return table
227
228 def login(req):
229     helper = grmfrontend.GrmPageHelper(req)
230
231     # only allow https access to the login page
232     if not helper.refuse_http():
233         return
234
235     logintypes = arizonaconfig.get_option("logintypes")
236     if len(logintypes) == 0:
237        return "No login types are enabled on this server.   You may not log in"
238
239     loginoptions = ""
240     default = True
241     for logintype in logintypes:
242         if "PLauthenticate" == logintype:
243             loginoptions += OPTION(logintype, default, "PlanetLab")
244         elif "PLEauthenticate" == logintype:
245             loginoptions += OPTION(logintype, default, "PlanetLab Eurpoe")
246         elif "passfileauthenticate" == logintype:
247             loginoptions += OPTION(logintype, default, "Passfile")
248         default = False
249
250     loginbody = ""
251
252     if loginoptions:
253         loginaccount = ""
254         loginaccount += "<h3> ... or ...<br>Login using a planetlab account</h3>"
255         loginaccount += TABLE(TR(TD("Site:") + TD(SELECT("authmethod", loginoptions))) + \
256                            TR(TD("Username:") + TD(INPUT("username"))) + \
257                            TR(TD("Password:") + TD(INPUT("password", type="password"))))
258         loginaccount += BUTTON("submit", "Login")
259         loginaccount = FORM("gacks_submit_auth_plc", "POST", loginaccount)
260         loginbody += loginaccount
261
262     req.write( arizonatemplate.retrieve_template("gacks_login.stemp", {'loginbody':loginbody}) )
263
264     helper.done()
265
266 def logout(req):
267     helper = grmfrontend.GrmPageHelper(req)
268     helper.set_logged_in(False)
269     helper.redirect(ROOT)
270     helper.done()
271
272 def gacks_submit_auth_plc(req, username = None, password = None, authmethod = "PLauthenticate", statusonly=False):
273     helper = grmfrontend.GrmPageHelper(req)
274
275     try:
276         do_auth(helper, username, password, authmethod, None)
277         helper.plc_to_geni()
278     except LoginError, e:
279         if statusonly:
280             req.write("Error: " + e.value)
281             helper.done()
282             return
283         else:
284             req.write( arizonatemplate.retrieve_template("gacks_login_error.stemp",
285                                             {"errormessage":str(e.value)}) )
286             helper.done()
287             return
288
289     if statusonly:
290         req.write("Success")
291         helper.done()
292     else:
293         # redirect the user back to the home page
294         helper.redirect(ROOT)
295         helper.done()
296
297 def gacks_submit_auth(req, infile, statusonly=False):
298     helper = grmfrontend.GrmPageHelper(req)
299
300     try:
301         do_auth_geni(helper, infile)
302     except LoginError, e:
303         if statusonly:
304             req.write("Error: " + e.value)
305             helper.done()
306             return
307         else:
308             req.write( arizonatemplate.retrieve_template("gacks_login_error.stemp",
309                                             {"errormessage":str(e.value)}) )
310             helper.done()
311             return
312
313     if statusonly:
314         req.write("Success")
315         helper.done()
316     else:
317         # redirect the user back to the home page
318         helper.redirect(ROOT)
319         helper.done()
320
321 def debug(req):
322     helper = grmfrontend.GrmPageHelper(req)
323
324     debugInfo = "current time: " + str(time.time()) + " " + time.ctime(time.time()) + "\n"
325
326     debugInfo += "interpreter: " + str(req.interpreter) + "\n"
327     debugInfo += "uri: " + str(req.uri) + "\n"
328     debugInfo += "hostname: " + str(req.hostname) + "\n"
329     debugInfo += "connection.local_host,local_ip: " + str(req.connection.local_host) + "," + str(req.connection.local_ip) + "\n"
330     debugInfo += "parsed_uri: " + str(req.parsed_uri) + "\n"
331
332     req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Debug Information'}) + \
333            "<pre>" + debugInfo + "\n" + "</pre>" + \
334            arizonatemplate.retrieve_template("gacks_footer.stemp") )
335
336     helper.done()
337
338 def edit(req, id, timeStart, timeStop, allocator=None):
339     helper = grmfrontend.GrmPageHelper(req)
340     if not helper.check_login():
341         return
342
343     name = str(helper.get_hrn())
344     table = create_detail_table(helper, id, timeStart, timeStop, allocator)
345
346     handlehiddenfields = INPUT("id", "hidden", 30, id) + \
347                          INPUT("timeStart", "hidden", 30, timeStart) + \
348                          INPUT("timeStop", "hidden", 30, timeStop) + \
349                          INPUT("allocator", "hidden", 30, allocator) 
350
351     req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Edit Reservation'}) + \
352                arizonatemplate.retrieve_template("gacks_detail.stemp",
353                                                     {'HRN':name,
354                                                      'DETAILTABLE':table,
355                                                      'HANDLEHIDDENFIELDS':handlehiddenfields}) + \
356                arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
357
358     helper.done()
359
360 def delete(req, id, timeStart, timeStop, allocator=None):
361     helper = grmfrontend.GrmPageHelper(req)
362     if not helper.check_login():
363         return
364
365     recList = helper.get_gacks_client().query_exact(id = id, timeStart=timeStart, timeStop=timeStop, hasAllocator = allocator)
366
367     handles = records_to_handles(recList)
368
369     helper.get_gacks_client().set_allocator(helper.get_grm_gid(), handles, None, -1, True)
370     helper.get_gacks_client().set_consumer(helper.get_grm_gid(), handles, None)
371
372     req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Reservation Deleted'}) + \
373                arizonatemplate.retrieve_template("gacks_result.stemp",
374                                                     {'MESSAGE':"reservation deleted"}) + \
375                arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
376
377     helper.done()
378
379 def reserve(req, id="cpu", y=None, m=None, d=None, hour="0", duration="1", timezone=None):
380     helper = grmfrontend.GrmPageHelper(req)
381     if not helper.check_login():
382         return
383
384     if (timezone != None):
385         helper.set_timezone(timezone);
386
387     #changeFunc = "javascript: if (document.getElementById('id').value=='cores') { ShowMenu(2, 'divUnits', 2); } else { ShowMenu(1, 'divUnits', 2); }"
388
389     changeFunc = "reload('gacksreserveform');"
390
391     name = str(helper.get_hrn())
392     idselect = create_id_select(id, onChange=changeFunc) #+ " " + BUTTON("submit_update_id", "update")
393     unitinput = INPUT("units")
394     nodeinput = INPUT("nodes")
395     tstartinput = INPUT("timestart")
396     tstopinput = INPUT("timestop")
397     durationinput = INPUT("duration", size=4, value=duration)
398     consumerinput = INPUTFILE("consumerfile")
399     consumerhrninput = INPUT("consumerhrn")
400     timezoneinput = TIMEZONE_DROPDOWN("timezone", helper.get_timezone(), onChange=changeFunc) #+ " " + BUTTON("submit_update_tz", "update")
401
402     #unit_multidiv = MULTIDIV( "divUnits", [INPUT("units"), INPUT("units") + INPUT("nodes")] )
403
404     hourtable = create_hour_table(helper, id, y, m, d, hour)
405     datetable = create_date_table(helper, y, m, d, onChange=changeFunc)
406
407     var_dict =  {'HRN':name,
408                  'IDSELECT':idselect,
409                  'UNITINPUT': unitinput,
410                  'NODEINPUT': nodeinput,
411                  'TSTARTINPUT':tstartinput,
412                  'TSTOPINPUT':tstopinput,
413                  'DURATIONINPUT':durationinput,
414                  'HOURTABLE':hourtable,
415                  'DATETABLE':datetable,
416                  'CONSUMERINPUT':consumerinput,
417                  'CONSUMERHRNINPUT':consumerhrninput,
418                  'TIMEZONEINPUT':timezoneinput}
419
420     if (id=="cores"):
421         resv_template = "gacks_reserve_cores.stemp"
422     else:
423         resv_template = "gacks_reserve.stemp"
424
425     req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Create Reservation'}) + \
426                arizonatemplate.retrieve_template(resv_template, var_dict) + \
427                arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
428
429     helper.done()
430
431 def read_credential_file(helper, infile):
432         try:
433            fdt, tmpfile_name = tempfile.mkstemp()
434            tmpfile = os.fdopen(fdt, 'wb')
435            shutil.copyfileobj(infile.file, tmpfile, 8192)
436            tmpfile.close()
437
438            kind = determine_geni_filekind(tmpfile_name)
439
440            if kind != "credential":
441                raise GrmError("file is not a credential file")
442
443            cred = Credential(filename = tmpfile_name)
444
445            cred_caller_gid = cred.get_gid_caller()
446            cred_object_gid = cred.get_gid_object()
447
448            # make sure the caller_gid of the credential is the same as the person
449            # we're talking to.
450
451            if not cred_caller_gid.is_pubkey(helper.get_gid().get_pubkey()):
452                raise GrmError("Credential caller_gid does not match your gid")
453
454            try:
455                cred.verify_chain(helper.get_trusted_cert_list())
456            except CertMissingParent, CertNotSignedByParent:
457                raise GrmError("cred not traceable to trusted root")
458
459            return (cred, cred_caller_gid, cred_object_gid)
460         finally:
461            os.remove(tmpfile_name)
462
463 def get_site(hrn):
464     parts = hrn.split(".")
465     if len(parts) < 2:
466         return None
467     else:
468         return parts[1]
469
470 def allocated_to_site(record, site, grm_hrn):
471     allocators = record.get_allocators()
472
473     for i,allocator in enumerate(allocators):
474         if (allocator == grm_hrn):
475             # if it's allocated to the GRM, then don't charge the site for the
476             # reservation. This handles the case where GRM is owned by
477             # plc.arizona
478             if (i == len(allocators)-1):
479                 return False
480
481             a = allocators[i+1]
482             return get_site(a) == site
483
484     return False
485
486 def check_policy(helper, gid, handles):
487     violations = []
488     debugInfo = ""
489
490     qty = count_records(handles)
491     if (qty < 10):
492         violations.append("minimum quantity of 10 units must be reserved");
493
494     debugInfo += TR(TD("policy qty:") + TD(str(qty)));
495
496     resource_hours_request = count_resource_hours(handles, time.time())
497
498     grm_hrn = helper.get_grm_gid().get_hrn()
499     site = get_site(gid.get_hrn())
500
501     existing_records_candidates  = helper.get_gacks_client().query_exact()
502     existing_records = []
503     for record in existing_records_candidates:
504         if (allocated_to_site(record, site, grm_hrn)):
505             existing_records.append(record)
506
507     resource_hours_existing = count_resource_hours(existing_records, time.time())
508
509     if (resource_hours_request + resource_hours_existing) > 5:
510         violations.append("resource hours of request (" + str(resource_hours_request) + \
511                           ") plus resource hours of site " + site + " (" + str(resource_hours_existing) + ") is greater than 5")
512
513     debugInfo += TR(TD("policy site:") + TD(site))
514     debugInfo += TR(TD("policy res-hr request:") + TD(str(resource_hours_request)))
515     debugInfo += TR(TD("policy res-hr exist:") + TD(str(resource_hours_existing)))
516
517     return (violations, debugInfo)
518
519 def submit_reserve(req, consumerfile=None, consumerhrn=None, id=None, units=None, debug=False, noreserve=False, y=None, m=None, d=None, hour=None, duration="1", submit="reserve", statusonly=False, ignorepolicy=False, timezone=None):
520     helper = grmfrontend.GrmPageHelper(req)
521     if not helper.check_login():
522         return
523
524     if submit=="update":
525         return helper.redirect(ROOT + "reserve" + QUERY({'y': y, 'm': m, 'd': d, 'id': id, 'timezone': timezone}))
526
527     try:
528         timeStart = ymd_to_time(helper, y, m, d, hour)
529         timeStop = timeStart + int(duration) * 60 * 60
530
531         timeStart -= int(helper.get_timezone() * 60 * 60)
532         timeStop -= int(helper.get_timezone() * 60 * 60)
533
534         receipts = []
535         debugInfo = ""
536
537         rspec = {'id': id, 'timeStart': timeStart, 'timeStop': timeStop}
538
539         parts = units.split('-')
540         if len(parts)==2:
541             rspec['unitStart'] = parts[0]
542             rspec['unitStop'] = parts[1]
543         elif len(parts)==1:
544             rspec['unitQuantity'] = parts[0]
545             rspec['isLastAllocator'] = helper.get_grm_gid().get_hrn()
546         else:
547             helper.backpage("quantity specification syntax is bad")
548             return
549
550         if consumerfile:
551             (cred, cred_caller_gid, cred_object_gid) = read_credential_file(helper, consumerfile)
552
553         handles = helper.get_gacks_client().get_handle(rspec)
554
555         (policy_violations, policy_debugInfo) = check_policy(helper, helper.get_gid(), handles)
556         debugInfo += policy_debugInfo
557
558         if (handles == []):
559             message = "failed to resolve request into non-empty a set of handles"
560         elif (not ignorepolicy) and (policy_violations != []):
561             message = "policy violation: " + ",".join(policy_violations)
562         elif noreserve:
563             message = "reservation not added"
564         else:
565             receipts = helper.get_gacks_client().set_allocator(helper.get_grm_gid(), handles, helper.get_gid(), -1, True)
566             if consumerfile:
567                 cReceipts = helper.get_gacks_client().set_consumer(helper.get_grm_gid(), handles, cred)
568                 receipts = receipts + cReceipts
569             elif consumerhrn:
570                 cReceipts = helper.get_gacks_client().set_consumer_hrn(helper.get_grm_gid(), handles, consumerhrn)
571                 receipts = receipts + cReceipts
572             message = "reservation added"
573
574         if debug:
575             debugInfo += TR(TD("rspec") + TD(str(rspec)))
576             for handle in handles:
577                 debugInfo += TR(TD("handle") + TD(handle.as_string()))
578             for receipt in receipts:
579                 debugInfo += TR(TD("receipt") + TD(receipt.save_to_string()))
580             if consumerfile:
581                 debugInfo += TR(TD("infile") + TD(consumerfile.filename))
582             if policy_violations:
583                 debugInfo += TR(TD("policy violations") + TD(policy_violations))
584             debugInfo = TABLE(debugInfo)
585             template = "gacks_result_debug.stemp"
586         else:
587             template = "gacks_result.stemp"
588
589         req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Reservation Submitted'}) + \
590                    arizonatemplate.retrieve_template(template,
591                                                         {'MESSAGE': message, 'DEBUGINFO':debugInfo}) + \
592                    arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
593
594         helper.done()
595     except GrmError, e:
596         report_error(helper, statusonly, e)
597
598 def submit_consumer(req, infile, consumerhrn, id, timeStart, timeStop, allocator=None, statusonly=False):
599     helper = grmfrontend.GrmPageHelper(req)
600     if not helper.check_login():
601         return
602
603     try:
604         if (infile) and (not isinstance(infile, str)) and (infile.filename):
605             (cred, cred_caller_gid, cred_object_gid) = read_credential_file(helper, infile)
606         elif (consumerhrn):
607             cred = None
608         else:
609            raise GrmError("Must provide a Consumer Credential filename or HRN")
610
611         recList = helper.get_gacks_client().query_exact(id = id, timeStart=timeStart, timeStop=timeStop, hasAllocator = allocator)
612
613         handles = records_to_handles(recList)
614
615         if cred:
616             helper.get_gacks_client().set_consumer(helper.get_grm_gid(), handles, cred)
617         else:
618             helper.get_gacks_client().set_consumer_hrn(helper.get_grm_gid(), handles, consumerhrn)
619
620         req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Consumer Submitted'}) + \
621                    arizonatemplate.retrieve_template("gacks_result.stemp",
622                                                         {'MESSAGE':"consmer set"}) + \
623                    arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
624
625         helper.done()
626
627     except GrmError, e:
628         report_error(helper, statusonly, e)
629
630 def reserve_asap(req, duration="1"):
631     helper = grmfrontend.GrmPageHelper(req)
632     if not helper.check_login():
633         return
634
635     name = str(helper.get_hrn())
636     durationinput = INPUT("duration", size=4, value=duration)
637     consumerinput = INPUTFILE("consumerfile")
638     consumerhrninput = INPUT("consumerhrn")
639     asaptable = create_asap_table(helper)
640
641     req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Create Reservation'}) + \
642                arizonatemplate.retrieve_template("gacks_reserve_asap.stemp",
643                                                     {'HRN':name,
644                                                      'DURATIONINPUT':durationinput,
645                                                      'CONSUMERINPUT':consumerinput,
646                                                      'CONSUMERHRNINPUT':consumerhrninput,
647                                                      'ASAPTABLE':asaptable}) + \
648                arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
649
650     helper.done()
651
652 def submit_reserve_asap(req, consumerfile=None, consumerhrn=None, cpu="0", disk="0", network="0", debug=False, noreserve=False, duration="1", submit="reserve", statusonly=False, ignorepolicy=False):
653     helper = grmfrontend.GrmPageHelper(req)
654     if not helper.check_login():
655         return
656
657     try:
658         receipts = []
659         debugInfo = ""
660
661         cpu = int_default(cpu, 0)
662         disk = int_default(disk, 0)
663         network = int_default(network, 0)
664
665         job = AsapJob()
666         if (cpu > 0):
667             job.add(AsapResource(id="cpu", qty=cpu))
668         if (disk > 0):
669             job.add(AsapResource(id="disk", qty=disk))
670         if (network > 0):
671             job.add(AsapResource(id="network", qty=network))
672
673         if consumerfile:
674             (cred, cred_caller_gid, cred_object_gid) = read_credential_file(helper, consumerfile)
675             consumerhrn = cred_object_gid.get_hrn()
676         elif consumerhrn:
677             cred = None
678             cred_caller_gid = None
679             cred_object_gid = None
680         else:
681             raise GrmError("Must provide a Consumer Credential filename or HRN")
682
683         job.set_duration(int_default(duration, 0) * 60 * 60)
684         job.set_allocator(helper.get_gid().get_hrn())
685         job.set_consumer(consumerhrn)
686
687         debugInfo += TR(TD("duration") + TD(str(job.get_duration())))
688         debugInfo += TR(TD("resources") + TD(job.get_resources_string()))
689         debugInfo += TR(TD("allocator HRN") + TD(str(job.get_allocator())))
690         debugInfo += TR(TD("consumer HRN") + TD(consumerhrn))
691
692         helper.get_gacks_client().add_asap(helper.get_grm_gid(), job)
693
694         message = "asap job added"
695
696         if debug:
697             if consumerfile:
698                 debugInfo += TR(TD("infile") + TD(consumerfile.filename))
699             debugInfo = TABLE(debugInfo)
700             template = "gacks_result_debug.stemp"
701         else:
702             template = "gacks_result.stemp"
703
704         req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Asap Job Submitted'}) + \
705                    arizonatemplate.retrieve_template(template,
706                                                         {'MESSAGE': message, 'DEBUGINFO':debugInfo}) + \
707                    arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
708
709         helper.done()
710     except GrmError, e:
711         report_error(helper, statusonly, e)
712
713 def delete_asap(req, jobid):
714     helper = grmfrontend.GrmPageHelper(req)
715     if not helper.check_login():
716         return
717
718     recList = helper.get_gacks_client().delete_asap(helper.get_grm_gid(), id=jobid)
719
720     req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Asap Reservation Deleted'}) + \
721                arizonatemplate.retrieve_template("gacks_result.stemp",
722                                                     {'MESSAGE':"asap reservation deleted"}) + \
723                arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
724
725     helper.done()
726
727 def download_big_obj(req, name):
728     helper = grmfrontend.GrmPageHelper(req)
729     if not helper.check_login():
730         return
731
732     log = gackslog_mysql.GacksMysqlLogger(directory = LOG_DIR, readonly=True)
733
734     data = log.query_big_obj(name)
735
736     req.write(str(data))
737
738 def format_log_gid(log, name):
739     name = name.strip("'")
740     name = name.strip("^")
741     gidStr = log.query_big_obj(name)
742
743     if (gidStr == None) or (gidStr == "None"):
744         return "None"
745
746     try:
747         gid = GID(string=gidStr)
748     except:
749         hrn = "gid_decode_error"
750
751     try:
752         hrn = gid.get_hrn()
753     except:
754         hrn = "get_hrn_error"
755
756     if hrn == None:
757         return LINK("download_big_obj?name="+name, "null_hrn")
758     else:
759         return LINK("download_big_obj?name="+name, hrn)
760
761 def format_log_receipt(log, name):
762     name = name.strip("'")
763     name = name.strip("^")
764     return LINK("download_big_obj?name="+name, "receipt")
765
766 def view_log(req, id=None, limit=25):
767     maxid=None
768     minid=None
769
770     helper = grmfrontend.GrmPageHelper(req)
771     if not helper.check_login():
772         return
773
774     log = gackslog_mysql.GacksMysqlLogger(directory = LOG_DIR, readonly=True)
775
776     table = TR(TH("id", align="left") + TH("timestamp", align="left") + TH("kind", align="left") + TH("function", align="left") + TH("msg", align="left"))
777
778     dictList = log.query(descending=True, beforeId=id, limit=limit)
779     for item in dictList:
780         argStrs = []
781         if item.get("args"):
782             for arg in item.get("args",[]):
783                 (name,value) = arg.split("=",1)
784                 if (name == "allocatorGID_str"):
785                     name = "allocator"
786                     value = format_log_gid(log, value)
787                 if (name == "callerGID_str"):
788                     name = "caller"
789                     value = format_log_gid(log, value)
790                 argStrs.append(name + "=" + str(value))
791
792         # if results contains receipts, then reformat them to somethings
793         # presentable to the user
794         results = item.get("result", None)
795         if isinstance(results, str) and (results[0]=='['):
796             results = eval(results)
797             resultStrs = []
798             for value in results:
799                 if (value[0] == "^") and (value[-8:] == ".receipt"):
800                     value = format_log_receipt(log, value)
801                 resultStrs.append(value)
802             results = "[" + ",".join(resultStrs) + "]"
803
804         msg = str(item.get("msg", None))
805         if (msg=="None"):
806             msg = ""
807
808         id = item.get("id",0)
809         if (id>maxid) or (maxid==None):
810             maxid = id
811         if (id<minid) or (minid==None):
812             minid = id
813
814         table = table + TR(TD(str(id)) + \
815                            TD(time.ctime(item.get("timestamp",0))) + \
816                            TD(str(item.get("kind",None))) + \
817                            TD(str(item.get("funcname",None))) + \
818                            TD(msg))
819
820         if (argStrs != ["None"]) and (argStrs != ["'None'"]) and (argStrs != []):
821             table = table + TR(TD("") + TD("<b>args:</b> " + ",<br>".join(argStrs),colspan=4))
822
823         if (results != "None") and (results != "'None'"):
824             table = table + TR(TD("") + TD("<b>result:</b> " + str(results),colspan=4))
825
826     table = TABLE(table)
827
828     if maxid!=None and limit!=None:
829         # assumes descending
830         nextlink = LINK("view_log?id=" + str(maxid+int(limit)) + "&limit=" + str(limit), "newer")
831     else:
832         nextlink = ""
833
834     if minid!=None and limit!=None:
835         # assumes descending
836         prevlink = LINK("view_log?id=" + str(max(int(minid),int(limit))) + "&limit=" + str(limit), "older")
837     else:
838         prevlink = ""
839
840     req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Gacks Audit Log'}) + \
841                arizonatemplate.retrieve_template("gacks_viewlog.stemp",
842                                                     {'LOGTABLE': table,
843                                                      'NEXTLINK': nextlink,
844                                                      'PREVLINK': prevlink}) + \
845                arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
846
847
848 # stolen from gacksnm.py
849 def update_to_resv(recList):
850        # build a set of reservations for each (id, consumer) that map to a
851        # quantity
852        reservations = {}
853        for item in recList:
854            if item.overlaps_time(time.time()):
855                key = item.id + "#" + str(item.consumerHRN)
856                reservation = reservations.get(key, {"id": item.id, "consumer": item.consumerHRN, "qty": 0})
857                reservation["qty"] = reservation["qty"] + item.get_quantity()
858                reservations[key] = reservation
859
860        return reservations
861
862
863 def view_update(req):
864     maxid=None
865     minid=None
866
867     helper = grmfrontend.GrmPageHelper(req)
868     if not helper.check_login():
869         return
870
871     (result, updateHash, recList) = helper.get_gacks_client().get_update("NO_HASH")
872
873     reservations = update_to_resv(recList)
874
875     restable = ""
876     restable = TR(TH("id") + TH("qty") + TH("consumer"))
877     for key in reservations.keys():
878         rec = reservations[key]
879         restable = restable + TR( TD(rec["id"]) + TD(rec["qty"]) + TD(rec["consumer"]) )
880     restable = TABLE(restable)
881
882     table = ""
883     table = TR(TH("id") + TH("start") + TH("stop") + TH("allocators") + TH("consumer") + TH("qty"))
884     for rec in recList:
885         qty = rec.get_quantity()
886         table = table + TR( TD(rec.id) + TD(time_to_str(helper, rec.timeStart)) + TD(time_to_str(helper, rec.timeStop)) + TD(", ".join(rec.allocatorHRNs)) + TD(rec.consumerHRN) + TD(qty) )
887     table = TABLE(table)
888
889     req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Gacks Update Data'}) + \
890                arizonatemplate.retrieve_template("gacks_viewupdate.stemp",
891                                                     {'UPDATETABLE': table,
892                                                      'RESTABLE': restable}) + \
893                arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
894
895
896
897 def index(req, show="user", timezone=None):
898     helper = grmfrontend.GrmPageHelper(req)
899     if not helper.check_login():
900         return
901
902     if timezone:
903         helper.set_timezone(timezone)
904
905     timezoneinput = TIMEZONE_DROPDOWN("timezone", helper.get_timezone()) + " " + BUTTON("submit", "update")
906
907     showUser = LINK(ROOT + "?show=user", "user")
908     showGrm = LINK(ROOT + "?show=grm", "grm")
909     showAll = LINK(ROOT + "?show=all", "all")
910
911     if (show=="user"):
912         allocator = helper.get_gid().get_hrn()
913         showUser = B(showUser)
914     elif (show=="grm"):
915         allocator = helper.get_grm_gid().get_hrn()
916         showGrm = B(showGrm)
917     elif (show=="all"):
918         allocator = None
919         showAll = B(showAll)
920     else:
921         allocator = show
922
923     showBar = showUser + " " + showGrm + " " + showAll
924
925     name = str(helper.get_hrn())
926     table = create_handle_table(helper, allocator)
927     asap_table = create_asap_job_table(helper, allocator)
928
929     req.write( arizonatemplate.retrieve_template("gacks_header.stemp", {'TITLE':'Reservation List'}) + \
930                arizonatemplate.retrieve_template("gacks_home.stemp", {'HRN':name, 'HANDLETABLE':table, 'ASAPJOBTABLE': asap_table, 'SHOWBAR':showBar, 'TIMEZONEINPUT': timezoneinput}) + \
931                arizonatemplate.retrieve_template("gacks_footer.stemp", {}) )
932
933     helper.done()
934
935
936