def css_tmpl(configuration, user_settings): """Stylesheets to include in the page header""" # TODO: can we update style inline to avoid explicit themed_styles? css = themed_styles( configuration, base=['jquery.contextmenu.css', 'jquery.managers.contextmenu.css'], user_settings=user_settings) return css
def css_tmpl(configuration): """Stylesheets to include in the page header""" css = themed_styles(configuration, base=['jquery.contextmenu.css', 'jquery.xbreadcrumbs.css', 'jquery.fmbreadcrumbs.css', 'jquery.fileupload.css', 'jquery.fileupload-ui.css'], skin=['fileupload-ui.custom.css', 'xbreadcrumbs.custom.css']) css['advanced'] += ''' <link href="/images/lib/noUiSlider/jquery.nouislider.css" rel="stylesheet" type="text/css" /> ''' css['advanced'] += advanced_editor_css_deps() return css
def bailout_helper(configuration, backend, out_obj, title_text="Runtime Error", header_text="Internal Error"): """Fall back output helper to init basic emergency output""" _logger = configuration.logger if out_obj is None: out_obj = [] title = {'object_type': 'title', 'text': title_text} # Try hard to display something mildly formatted try: from shared.html import themed_styles, themed_scripts title['style'] = themed_styles(configuration) title['script'] = themed_scripts(configuration, logged_in=False) # Hide menu to avoid message truncation title['skipmenu'] = True except Exception, exc: _logger.error("failed to provide even basic styling for %s" % backend)
def css_tmpl(configuration, user_settings={}): """Stylesheets to include in the page header""" # TODO: can we update style inline to avoid explicit themed_styles? css = themed_styles( configuration, advanced=[ 'jquery.contextmenu.css', 'jquery.managers.contextmenu.css', 'jquery.xbreadcrumbs.css', 'jquery.fmbreadcrumbs.css', 'jquery.fileupload.css', 'jquery.fileupload-ui.css' ], skin=['fileupload-ui.custom.css', 'xbreadcrumbs.custom.css'], user_settings=user_settings) css['advanced'] += ''' <link href="/images/lib/noUiSlider/jquery.nouislider.css" rel="stylesheet" type="text/css" /> <link href="/images/lib/ParaView/Visualizer/main.css" rel="stylesheet" type="text/css" /> ''' css['advanced'] += advanced_editor_css_deps() return css
def create_monitor(vgrid_name): """Write monitor HTML file for vgrid_name""" html_file = os.path.join(configuration.vgrid_home, vgrid_name, '%s.html' % configuration.vgrid_monitor) print 'collecting statistics for VGrid %s' % vgrid_name sleep_secs = configuration.sleep_secs slackperiod = configuration.slackperiod now = time.asctime(time.localtime()) html_vars = { 'sleep_secs': sleep_secs, 'vgrid_name': vgrid_name, 'logo_url': '/images/logo.jpg', 'now': now, 'short_title': configuration.short_title, } monitor_meta = '''<meta http-equiv="refresh" content="%(sleep_secs)s" /> ''' % html_vars add_import = ''' <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> ''' add_init = '' add_ready = ''' // table initially sorted by col. 1 (name) var sortOrder = [[1,0]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("table.monitor").tablesorter({widgets: ["zebra"], textExtraction: imgTitle, }); $("table.monitor").each(function () { try { $(this).trigger("sorton", [sortOrder]); } catch(err) { /* tablesorter chokes on empty tables - just continue */ } }); ''' monitor_js = ''' %s <script type="text/javascript" > %s $(document).ready(function() { %s } ); </script> ''' % (add_import, add_init, add_ready) # User default site style style_helpers = themed_styles(configuration) script_helpers = themed_scripts(configuration) script_helpers['advanced'] += add_import script_helpers['init'] += add_init script_helpers['ready'] += add_ready html = get_xgi_html_header( configuration, '%(short_title)s Monitor, VGrid %(vgrid_name)s' % html_vars, '', html=True, meta=monitor_meta, style_map=style_helpers, script_map=script_helpers, frame=False, menu=False, widgets=False, userstyle=False, ) html += \ ''' <!-- end of raw header: this line is used by showvgridmonitor --> <h1>Statistics/monitor for the %(vgrid_name)s VGrid</h1> <div class="generatornote smallcontent"> This page was generated %(now)s (automatic refresh every %(sleep_secs)s secs). </div> '''\ % html_vars # loop and get totals parse_count = 0 queued_count = 0 frozen_count = 0 executing_count = 0 finished_count = 0 failed_count = 0 retry_count = 0 canceled_count = 0 cpucount_requested = 0 cpucount_done = 0 nodecount_requested = 0 nodecount_done = 0 cputime_requested = 0 cputime_done = 0 used_walltime = 0 disk_requested = 0 disk_done = 0 memory_requested = 0 memory_done = 0 runtimeenv_dict = {'': 0} runtimeenv_requested = 0 runtimeenv_done = 0 number_of_jobs = 0 up_count = 0 down_count = 0 slack_count = 0 job_assigned = 0 job_assigned_cpus = 0 gstat = GridStat(configuration, logger) runtimeenv_dict = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'RUNTIMEENVIRONMENT', {}) parse_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'PARSE') queued_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'QUEUED') frozen_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'FROZEN') executing_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'EXECUTING') failed_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'FAILED') retry_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'RETRY') canceled_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'CANCELED') expired_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'EXPIRED') finished_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'FINISHED') nodecount_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'NODECOUNT_REQ') nodecount_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'NODECOUNT_DONE') cputime_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'CPUTIME_REQ') cputime_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'CPUTIME_DONE') used_walltime = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'USED_WALLTIME') if (used_walltime == 0): used_walltime = datetime.timedelta(0) used_walltime = format_timedelta(used_walltime) disk_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'DISK_REQ') disk_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'DISK_DONE') memory_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'MEMORY_REQ') memory_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'MEMORY_DONE') cpucount_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'CPUCOUNT_REQ') cpucount_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'CPUCOUNT_DONE') runtimeenv_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'RUNTIMEENVIRONMENT_REQ') runtimeenv_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), 'RUNTIMEENVIRONMENT_DONE') number_of_jobs = parse_count number_of_jobs += queued_count number_of_jobs += frozen_count number_of_jobs += expired_count number_of_jobs += canceled_count number_of_jobs += failed_count number_of_jobs += executing_count number_of_jobs += finished_count number_of_jobs += retry_count html_vars = { 'parse_count': parse_count, 'queued_count': queued_count, 'frozen_count': frozen_count, 'executing_count': executing_count, 'failed_count': failed_count, 'retry_count': retry_count, 'canceled_count': canceled_count, 'expired_count': expired_count, 'finished_count': finished_count, 'number_of_jobs': number_of_jobs, 'cpucount_requested': cpucount_requested, 'cpucount_done': cpucount_done, 'nodecount_requested': nodecount_requested, 'nodecount_done': nodecount_done, 'cputime_requested': cputime_requested, 'cputime_done': cputime_done, 'used_walltime': used_walltime, 'disk_requested': disk_requested, 'disk_done': disk_done, 'memory_requested': memory_requested, 'memory_done': memory_done, 'runtimeenv_requested': runtimeenv_requested, 'runtimeenv_done': runtimeenv_done, } html += \ """<h2>Job Stats</h2><table class=monitorstats><tr><td> <table class=monitorjobs><tr class=title><td>Job State</td><td>Number of jobs</td></tr> <tr><td>Parse</td><td>%(parse_count)s</td></tr> <tr><td>Queued</td><td>%(queued_count)s</td></tr> <tr><td>Frozen</td><td>%(frozen_count)s</td></tr> <tr><td>Executing</td><td>%(executing_count)s</td></tr> <tr><td>Failed</td><td>%(failed_count)s</td></tr> <tr><td>Retry</td><td>%(retry_count)s</td></tr> <tr><td>Canceled</td><td>%(canceled_count)s</td></tr> <tr><td>Expired</td><td>%(expired_count)s</td></tr> <tr><td>Finished</td><td>%(finished_count)s</td></tr> <tr><td>Total</td><td>%(number_of_jobs)s</td></tr> </table> </td><td> <table class=monitorresreq> <tr class=title><td>Requirement</td><td>Requested</td><td>Done</td></tr> <tr><td>Cpucount</td><td>%(cpucount_requested)s</td><td>%(cpucount_done)s</td></tr> <tr><td>Nodecount</td><td>%(nodecount_requested)s</td><td>%(nodecount_done)s</td></tr> <tr><td>Cputime</td><td>%(cputime_requested)s</td><td>%(cputime_done)s</td></tr> <tr><td>GB Disk</td><td>%(disk_requested)s</td><td>%(disk_done)s</td></tr> <tr><td>MB Memory</td><td>%(memory_requested)s</td><td>%(memory_done)s</td></tr> <tr><td>Runtime Envs</td><td>%(runtimeenv_requested)s</td><td>%(runtimeenv_done)s</td></tr> <tr><td>Used Walltime</td><td colspan='2'>%(used_walltime)s</td></tr> </table><br /> </td><td> <div class=monitorruntimeenvdetails> <table class=monitorruntimeenvdone> <tr class=title><td>Runtime Envs Done</td><td></td></tr> """\ % html_vars if len(runtimeenv_dict.keys()) < 1: # No runtimeenv requests html += '<tr><td></td><td>-</td></tr>\n' else: for entry in runtimeenv_dict.keys(): if not entry == '': html += '<tr><td>' + entry + '</td><td>'\ + str(runtimeenv_dict[entry]) + '</td></tr>\n' total_number_of_exe_resources, total_number_of_store_resources = 0, 0 total_number_of_exe_cpus, total_number_of_store_gigs = 0, 0 vgrid_name_list = vgrid_name.split('/') current_dir = '' exes, stores = '', '' for vgrid_name_part in vgrid_name_list: current_dir = os.path.join(current_dir, vgrid_name_part) abs_mon_dir = os.path.join(configuration.vgrid_home, current_dir) # print 'dir: %s' % abs_mon_dir # Potential race - just ignore if it disappeared try: sorted_names = os.listdir(abs_mon_dir) except OSError: continue sorted_names.sort() for filename in sorted_names: # print filename if filename.startswith('monitor_last_request_'): # read last request helper file mon_file_name = os.path.join(abs_mon_dir, filename) print 'found ' + mon_file_name last_request_dict = unpickle(mon_file_name, logger) if not last_request_dict: print 'could not open and unpickle: '\ + mon_file_name continue if not last_request_dict.has_key('CREATED_TIME'): print 'skip broken last request dict: '\ + mon_file_name continue difference = datetime.datetime.now()\ - last_request_dict['CREATED_TIME'] days = str(difference.days) hours = str(difference.seconds / 3600) minutes = str((difference.seconds % 3600) / 60) seconds = str((difference.seconds % 60) % 60) last_timetuple = last_request_dict['CREATED_TIME'].timetuple() if last_request_dict.has_key('CPUTIME'): cputime = last_request_dict['CPUTIME'] elif last_request_dict.has_key('cputime'): cputime = last_request_dict['cputime'] else: print 'ERROR: last request does not contain cputime field!: %s'\ % last_request_dict continue try: cpusec = int(cputime) except ValueError: try: cpusec = int(float(cputime)) except ValueError, verr: print 'ERROR: failed to parse cputime %s: %s'\ % (cputime, verr) # Include execution delay guesstimate for strict fill # LRMS resources try: delay = int(last_request_dict['EXECUTION_DELAY']) except KeyError: delay = 0 except ValueError: delay = 0 time_remaining = (last_request_dict['CREATED_TIME'] + datetime.timedelta(seconds=cpusec) + datetime.timedelta(seconds=delay))\ - datetime.datetime.now() days_rem = str(time_remaining.days) hours_rem = str(time_remaining.seconds / 3600) minutes_rem = str((time_remaining.seconds % 3600) / 60) seconds_rem = str((time_remaining.seconds % 60) % 60) if time_remaining.days < -7: try: print 'removing: %s as we havent seen him for %s days.'\ % (mon_file_name, abs(time_remaining).days) os.remove(mon_file_name) except Exception, err: print "could not remove: '%s' Error: %s"\ % (mon_file_name, str(err)) pass else: unique_res_name_and_exe_list = \ filename.split('monitor_last_request_', 1) if cpusec == 0: resource_status = 'unavailable' elif time_remaining.days < 0: # time_remaining.days < 0 means that we have passed the specified time time_rem_abs = abs(time_remaining) if time_rem_abs.days == 0\ and int(time_rem_abs.seconds)\ < int(slackperiod): resource_status = 'slack' slack_count = slack_count + 1 else: resource_status = 'offline' down_count = down_count + 1 else: resource_status = 'online' up_count = up_count + 1 exes += '<tr>' exes += \ '<td><img src=/images/status-icons/%s.png /></td>'\ % resource_status public_id = unique_res_name_and_exe_list[1] if last_request_dict['RESOURCE_CONFIG'].get( 'ANONYMOUS', True): public_id = anon_resource_id(public_id) public_name = last_request_dict['RESOURCE_CONFIG'].get( 'PUBLICNAME', '') resource_parts = public_id.split('_', 2) resource_name = "<a href='viewres.py?unique_resource_name=%s'>%s</a>" % \ (resource_parts[0], resource_parts[0]) if public_name: resource_name += "<br />(alias %s)" % public_name else: resource_name += "<br />(no alias)" resource_name += "<br />%s" % resource_parts[1] exes += '<td>%s</td>' % resource_name last_asctime = time.asctime(last_timetuple) last_epoch = time.mktime(last_timetuple) exes += '<td><div class="sortkey">%s</div>%s<br />' % \ (last_epoch, last_asctime) exes += '(%sd %sh %sm %ss ago)</td>' % (days, hours, minutes, seconds) exes += '<td>' + vgrid_name + '</td>' runtime_envs = last_request_dict['RESOURCE_CONFIG'][ 'RUNTIMEENVIRONMENT'] runtime_envs.sort() re_list_text = ', '.join([i[0] for i in runtime_envs]) exes += '<td title="%s">' % re_list_text \ + str(len(runtime_envs)) + '</td>' exes += '<td>'\ + str(last_request_dict['RESOURCE_CONFIG' ]['CPUTIME']) + '</td><td>'\ + str(last_request_dict['RESOURCE_CONFIG' ]['NODECOUNT']) + '</td><td>'\ + str(last_request_dict['RESOURCE_CONFIG' ]['CPUCOUNT']) + '</td><td>'\ + str(last_request_dict['RESOURCE_CONFIG' ]['DISK']) + '</td><td>'\ + str(last_request_dict['RESOURCE_CONFIG' ]['MEMORY']) + '</td><td>'\ + str(last_request_dict['RESOURCE_CONFIG' ]['ARCHITECTURE']) + '</td>' exes += '<td>' + last_request_dict['STATUS']\ + '</td><td>' + str(last_request_dict['CPUTIME' ]) + '</td>' exes += '<td class=status_%s>' % resource_status if 'unavailable' == resource_status: exes += '-' elif 'slack' == resource_status: exes += 'Within slack period (%s < %s secs)'\ % (time_rem_abs.seconds, slackperiod) elif 'offline' == resource_status: exes += 'down?' else: exes += '%sd, %sh, %sm, %ss'\ % (days_rem, hours_rem, minutes_rem, seconds_rem) exes += '</td>' exes += '</tr>\n' if last_request_dict['STATUS'] == 'Job assigned': job_assigned = job_assigned + 1 job_assigned_cpus = job_assigned_cpus\ + int(last_request_dict['RESOURCE_CONFIG' ]['NODECOUNT'])\ * int(last_request_dict['RESOURCE_CONFIG' ]['CPUCOUNT']) total_number_of_exe_resources += 1 total_number_of_exe_cpus += int( last_request_dict['RESOURCE_CONFIG']['NODECOUNT']) \ * int(last_request_dict['RESOURCE_CONFIG']['CPUCOUNT']) elif filename.startswith('monitor_last_status_'): # store must be linked to this vgrid, not only parent vgrid: # inheritance only covers access, not automatic participation if current_dir != vgrid_name: continue # read last resource action status file mon_file_name = os.path.join(abs_mon_dir, filename) print 'found ' + mon_file_name last_status_dict = unpickle(mon_file_name, logger) if not last_status_dict: print 'could not open and unpickle: '\ + mon_file_name continue if not last_status_dict.has_key('CREATED_TIME'): print 'skip broken last request dict: '\ + mon_file_name continue difference = datetime.datetime.now()\ - last_status_dict['CREATED_TIME'] days = str(difference.days) hours = str(difference.seconds / 3600) minutes = str((difference.seconds % 3600) / 60) seconds = str((difference.seconds % 60) % 60) if last_status_dict['STATUS'] == 'stopped': time_stopped = datetime.datetime.now() - \ last_status_dict['CREATED_TIME'] if time_stopped.days > 7: try: print 'removing: %s as we havent seen him for %s days.'\ % (mon_file_name, abs(time_stopped).days) os.remove(mon_file_name) except Exception, err: print "could not remove: '%s' Error: %s"\ % (mon_file_name, str(err)) continue
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) unique_res_names = accepted["unique_resource_name"] (re_stat, re_list) = list_runtime_environments(configuration) if not re_stat: logger.warning("Failed to load list of runtime environments") output_objects.append({"object_type": "error_text", "text": "Error getting list of runtime environments"}) return (output_objects, returnvalues.SYSTEM_ERROR) title_entry = find_entry(output_objects, "title") title_entry["text"] = "Resource Management" title_entry["style"] = themed_styles(configuration) title_entry[ "javascript" ] = """ <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > var toggleHidden = function(classname) { // classname supposed to have a leading dot $(classname).toggleClass("hidden"); } $(document).ready(function() { // init confirmation dialog $("#confirm_dialog").dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 500, buttons: { "Cancel": function() { $("#" + name).dialog("close"); } } }); } ); </script> """ output_objects.append({"object_type": "header", "text": " Resource Management"}) output_objects.append({"object_type": "sectionheader", "text": "%s Resources Owned" % configuration.short_title}) quick_links = [{"object_type": "text", "text": "Quick links to all your resources and individual management"}] quick_links.append({"object_type": "html_form", "text": '<div class="hidden quicklinks">'}) quick_links.append( { "object_type": "link", "destination": "javascript:toggleHidden('.quicklinks');", "class": "removeitemlink", "title": "Toggle view", "text": "Hide quick links", } ) quick_links.append({"object_type": "text", "text": ""}) quick_res = {} quick_links_index = len(output_objects) output_objects.append({"object_type": "sectionheader", "text": ""}) output_objects.append( { "object_type": "html_form", "text": """ <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea> </div> """, } ) owned = 0 res_map = get_resource_map(configuration) for unique_resource_name in res_map.keys(): if sandbox_resource(unique_resource_name): continue owner_list = res_map[unique_resource_name][OWNERS] resource_config = res_map[unique_resource_name][CONF] visible_res_name = res_map[unique_resource_name][RESID] if client_id in owner_list: quick_res[unique_resource_name] = { "object_type": "multilinkline", "links": [ { "object_type": "link", "destination": "?unique_resource_name=%s" % unique_resource_name, "class": "adminlink", "title": "Manage %s" % unique_resource_name, "text": "Manage %s" % unique_resource_name, }, { "object_type": "link", "destination": "viewres.py?unique_resource_name=%s" % visible_res_name, "class": "infolink", "title": "View %s" % unique_resource_name, "text": "View %s" % unique_resource_name, }, ], } if unique_resource_name in unique_res_names: raw_conf_file = os.path.join(configuration.resource_home, unique_resource_name, "config.MiG") try: filehandle = open(raw_conf_file, "r") raw_conf = filehandle.readlines() filehandle.close() except: raw_conf = [""] res_html = display_resource( unique_resource_name, raw_conf, resource_config, owner_list, re_list, configuration ) output_objects.append({"object_type": "html_form", "text": res_html}) output_objects.append({"object_type": "sectionheader", "text": "Retire resource"}) output_objects.append( { "object_type": "text", "text": """ Use the link below to permanently remove the resource from the grid after stopping all units and the front end. """, } ) js_name = "delres%s" % hexlify(unique_resource_name) helper = html_post_helper(js_name, "delres.py", {"unique_resource_name": unique_resource_name}) output_objects.append({"object_type": "html_form", "text": helper}) output_objects.append( { "object_type": "link", "destination": "javascript: confirmDialog(%s, '%s');" % (js_name, "Really delete %s? (fails if it is busy)" % unique_resource_name), "class": "removelink", "title": "Delete %s" % unique_resource_name, "text": "Delete %s" % unique_resource_name, } ) owned += 1 if owned == 0: output_objects.append({"object_type": "text", "text": "You are not listed as owner of any resources!"}) else: sorted_links = quick_res.items() sorted_links.sort() for (res_id, link_obj) in sorted_links: quick_links.append(link_obj) # add new line quick_links.append({"object_type": "text", "text": ""}) quick_links.append({"object_type": "html_form", "text": '</div><div class="quicklinks">'}) quick_links.append( { "object_type": "link", "destination": "javascript:toggleHidden('.quicklinks');", "class": "additemlink", "title": "Toggle view", "text": "Show quick links", } ) quick_links.append({"object_type": "html_form", "text": "</div>"}) output_objects = output_objects[:quick_links_index] + quick_links + output_objects[quick_links_index:] return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) vgrid_name = accepted['vgrid_name'][-1] if not vgrid_is_owner_or_member(vgrid_name, client_id, configuration): output_objects.append({'object_type': 'error_text', 'text': '''You must be an owner or member of %s vgrid to access the workflows.''' % vgrid_name}) return (output_objects, returnvalues.CLIENT_ERROR) title_entry = find_entry(output_objects, 'title') title_entry['text'] = '%s Workflows' \ % configuration.site_vgrid_label title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = \ ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript"> $(document).ready(function() { $("#logarea").scrollTop($("#logarea")[0].scrollHeight); // table initially sorted by 0 (last update / date) var sortOrder = [[0,1]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#workflowstable").tablesorter({widgets: ["zebra", "saveSort"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> ''' \ % default_pager_entries output_objects.append({'object_type': 'html_form', 'text': ''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="72" rows="10" id="confirm_input" style="display:none;"></textarea> </div> '''}) output_objects.append({'object_type': 'header', 'text': '%s Workflows for %s' % (configuration.site_vgrid_label, vgrid_name)}) logger.info('vgridworkflows %s' % vgrid_name) # Display active trigger jobs for this vgrid output_objects.append({'object_type': 'sectionheader', 'text': 'Active Trigger Jobs'}) html = '<table><thead><tr>' html += '<th>Job ID</th>' html += '<th>Rule</th>' html += '<th>Path</th>' html += '<th>Change</th>' html += '<th>Time</th>' html += '<th>Status</th>' html += '</tr></thead>' html += '<tbody>' trigger_job_dir = os.path.join(configuration.vgrid_home, os.path.join(vgrid_name, '.%s.jobs' % configuration.vgrid_triggers)) trigger_job_pending_dir = os.path.join(trigger_job_dir, 'pending_states') trigger_job_final_dir = os.path.join(trigger_job_dir, 'final_states' ) if makedirs_rec(trigger_job_pending_dir, logger) \ and makedirs_rec(trigger_job_final_dir, logger): abs_vgrid_dir = '%s/' \ % os.path.abspath(os.path.join(configuration.vgrid_files_home, vgrid_name)) for filename in os.listdir(trigger_job_pending_dir): trigger_job_filepath = \ os.path.join(trigger_job_pending_dir, filename) trigger_job = unpickle(trigger_job_filepath, logger) serverjob_filepath = \ os.path.join(configuration.mrsl_files_dir, os.path.join(client_id_dir(trigger_job['owner' ]), '%s.mRSL' % trigger_job['jobid'])) serverjob = unpickle(serverjob_filepath, logger) if serverjob: if serverjob['STATUS'] in pending_states: trigger_event = trigger_job['event'] trigger_rule = trigger_job['rule'] trigger_action = trigger_event['event_type'] trigger_time = time.ctime(trigger_event['time_stamp' ]) trigger_path = '%s %s' % (trigger_event['src_path' ].replace(abs_vgrid_dir, ''), trigger_event['dest_path' ].replace(abs_vgrid_dir, '')) html += \ '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></td><td>%s</td>' \ % (trigger_job['jobid'], trigger_rule['rule_id' ], trigger_path, trigger_action, trigger_time, serverjob['STATUS']) elif serverjob['STATUS'] in final_states: src_path = os.path.join(trigger_job_pending_dir, filename) dest_path = os.path.join(trigger_job_final_dir, filename) move_file(src_path, dest_path, configuration) else: logger.error('Trigger job: %s, unknown state: %s' % (trigger_job['jobid'], serverjob['STATUS'])) html += '</tbody>' html += '</table>' output_objects.append({'object_type': 'html_form', 'text': html}) # Display active trigger jobs for this vgrid output_objects.append({'object_type': 'sectionheader', 'text': 'Trigger Log'}) log_content = read_trigger_log(configuration, vgrid_name) output_objects.append({'object_type': 'html_form', 'text': ''' <div class="form_container"> <textarea id="logarea" rows=10 readonly="readonly">%s</textarea> </div> ''' % log_content}) output_objects.append({'object_type': 'sectionheader', 'text': 'Manage Triggers'}) # Always run as rule creator to avoid users being able to act on behalf # of ANY other user using triggers (=exploit) extra_fields = [ ('path', None), ('match_dirs', ['False', 'True']), ('match_recursive', ['False', 'True']), ('changes', [keyword_all] + valid_trigger_changes), ('action', [keyword_auto] + valid_trigger_actions), ('arguments', None), ('run_as', client_id), ] # NOTE: we do NOT show saved template contents - see addvgridtriggers optional_fields = [('rate_limit', None), ('settle_time', None)] (status, oobjs) = vgrid_add_remove_table( client_id, vgrid_name, 'trigger', 'vgridtrigger', configuration, extra_fields, optional_fields, ) output_objects.extend(oobjs) if not status: return (output_objects, returnvalues.SYSTEM_ERROR) return (output_objects, returnvalues.OK)
def initialize_main_variables(client_id, op_title=True, op_header=True, op_menu=True): """Script initialization is identical for most scripts in shared/functionalty. This function should be called in most cases. """ configuration = get_configuration_object() logger = configuration.logger output_objects = [] start_entry = make_start_entry() output_objects.append(start_entry) op_name = os.path.splitext(os.path.basename(requested_page()))[0] if op_title: skipwidgets = not configuration.site_enable_widgets or not client_id skipuserstyle = not configuration.site_enable_styling or not client_id title_object = make_title_entry('%s' % op_name, skipmenu=(not op_menu), skipwidgets=skipwidgets, skipuserstyle=skipuserstyle, skipuserprofile=(not client_id)) # Make sure base_menu is always set for extract_menu # Typicall overriden based on client_id cases below title_object['base_menu'] = configuration.site_default_menu output_objects.append(title_object) if op_header: header_object = make_header_entry('%s' % op_name) output_objects.append(header_object) if client_id: # add the user-defined menu and widgets (if possible) title = find_entry(output_objects, 'title') if title: settings = load_settings(client_id, configuration) # NOTE: loaded settings may be False rather than dict here if not settings: settings = {} title['style'] = themed_styles(configuration, user_settings=settings) title['script'] = themed_scripts(configuration, user_settings=settings) if settings: title['user_settings'] = settings base_menu = settings.get('SITE_BASE_MENU', 'default') if not base_menu in configuration.site_base_menu: base_menu = 'default' if base_menu == 'simple' and configuration.site_simple_menu: title['base_menu'] = configuration.site_simple_menu elif base_menu == 'advanced' and \ configuration.site_advanced_menu: title['base_menu'] = configuration.site_advanced_menu else: title['base_menu'] = configuration.site_default_menu user_menu = settings.get('SITE_USER_MENU', None) if configuration.site_user_menu and user_menu: title['user_menu'] = user_menu if settings.get('ENABLE_WIDGETS', True) and \ configuration.site_script_deps: user_widgets = load_widgets(client_id, configuration) if user_widgets: title['user_widgets'] = user_widgets user_profile = load_profile(client_id, configuration) if user_profile: # These data are used for display in own profile view only profile_image_list = user_profile.get('PUBLIC_IMAGE', []) if profile_image_list: # TODO: copy profile image to /public/avatars/X and use it profile_image = os.path.join( configuration.site_user_redirect, profile_image_list[-1]) else: profile_image = '' user_profile['profile_image'] = profile_image else: user_profile = {} # Always set full name for use in personal user menu full_name = extract_field(client_id, 'full_name') user_profile['full_name'] = full_name title['user_profile'] = user_profile logger.debug('setting user profile: %s' % user_profile) else: # No user so we just enforce default site style and scripts title = find_entry(output_objects, 'title') if title: title['style'] = themed_styles(configuration) title['script'] = themed_scripts(configuration, logged_in=False) return (configuration, logger, output_objects, op_name)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) status = returnvalues.OK defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) show_sandboxes = (accepted['show_sandboxes'][-1] != 'false') visible_exes = user_visible_res_exes(configuration, client_id) res_map = get_resource_map(configuration) anon_map = anon_to_real_res_map(configuration.resource_home) # Iterate through resources and show management for each one requested res_list = {'object_type': 'resource_list', 'resources': []} fields = ['PUBLICNAME', 'NODECOUNT', 'CPUCOUNT', 'MEMORY', 'DISK', 'ARCHITECTURE', 'SANDBOX', 'RUNTIMEENVIRONMENT'] # Leave the sorting to jquery tablesorter for visible_res_name in visible_exes.keys(): unique_resource_name = visible_res_name if visible_res_name in anon_map.keys(): unique_resource_name = anon_map[visible_res_name] if not show_sandboxes and sandbox_resource(unique_resource_name): continue res_obj = {'object_type': 'resource', 'name': visible_res_name} if client_id in res_map[unique_resource_name][OWNERS]: # Admin of resource when owner js_name = 'rmresowner%s' % hexlify(unique_resource_name) helper = html_post_helper(js_name, 'rmresowner.py', {'unique_resource_name': unique_resource_name, 'cert_id': client_id}) output_objects.append({'object_type': 'html_form', 'text': helper}) res_obj['resownerlink'] = \ {'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s');"\ % (js_name, 'Really leave %s owners?' % \ unique_resource_name), 'class': 'removelink', 'title': 'Leave %s owners' % unique_resource_name, 'text': ''} res_obj['resdetailslink'] = \ {'object_type': 'link', 'destination': 'resadmin.py?unique_resource_name=%s'\ % unique_resource_name, 'class': 'adminlink', 'title': 'Administrate %s' % unique_resource_name, 'text': ''} else: # link to become owner js_name = 'reqresowner%s' % hexlify(unique_resource_name) helper = html_post_helper(js_name, 'sendrequestaction.py', {'unique_resource_name': visible_res_name, 'request_type': 'resourceowner', 'request_text': ''}) output_objects.append({'object_type': 'html_form', 'text': helper}) res_obj['resownerlink'] = \ {'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s', '%s');"\ % (js_name, "Request ownership of " + \ visible_res_name + ":<br/>" + \ "\nPlease write a message to the owners (field below).", 'request_text'), 'class': 'addlink', 'title': 'Request ownership of %s' % visible_res_name, 'text': ''} res_obj['resdetailslink'] = \ {'object_type': 'link', 'destination': 'viewres.py?unique_resource_name=%s'\ % visible_res_name, 'class': 'infolink', 'title': 'View detailed %s specs' % \ visible_res_name, 'text': ''} # fields for everyone: public status for name in fields: res_obj[name] = res_map[unique_resource_name][CONF].get(name, '') # Use runtimeenvironment names instead of actual definitions res_obj['RUNTIMEENVIRONMENT'] = [i[0] for i in res_obj['RUNTIMEENVIRONMENT']] res_list['resources'].append(res_obj) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Resource management' # jquery support for tablesorter and confirmation on "leave": title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 500, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); // table initially sorted by col. 1 (admin), then 0 (name) var sortOrder = [[1,0],[0,0]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#resourcetable").tablesorter({widgets: ["zebra", "saveSort"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> ''' % default_pager_entries output_objects.append({'object_type': 'html_form', 'text':''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea> </div> ''' }) output_objects.append({'object_type': 'header', 'text': 'Available Resources' }) output_objects.append({'object_type': 'sectionheader', 'text' : 'Resources available on this server'}) output_objects.append({'object_type': 'text', 'text' : ''' All available resources are listed below with overall hardware specifications. Any resources that you own will have a administration icon that you can click to open resource management. ''' }) output_objects.append({'object_type': 'table_pager', 'entry_name': 'resources', 'default_entries': default_pager_entries}) output_objects.append(res_list) if configuration.site_enable_sandboxes: if show_sandboxes: output_objects.append({'object_type': 'link', 'destination': '?show_sandboxes=false', 'class': 'removeitemlink', 'title': 'Hide sandbox resources', 'text': 'Exclude sandbox resources', }) else: output_objects.append({'object_type': 'link', 'destination': '?show_sandboxes=true', 'class': 'additemlink', 'title': 'Show sandbox resources', 'text': 'Include sandbox resources', }) output_objects.append({'object_type': 'sectionheader', 'text' : 'Resource Status'}) output_objects.append({'object_type': 'text', 'text': ''' Live resource status is available in the resource monitor page with all %s/resources you can access ''' % configuration.site_vgrid_label}) output_objects.append({'object_type': 'link', 'destination': 'showvgridmonitor.py?vgrid_name=ALL', 'class': 'monitorlink', 'title': 'Show monitor with all resources you can access', 'text': 'Global resource monitor', }) output_objects.append({'object_type': 'sectionheader', 'text': 'Additional Resources' }) output_objects.append({'object_type': 'text', 'text': 'You can sign up spare or dedicated resources to the grid below.' }) output_objects.append({'object_type': 'link', 'destination' : 'resedit.py', 'class': 'addlink', 'title': 'Show sandbox resources', 'text': 'Create a new %s resource' % \ configuration.short_title, }) output_objects.append({'object_type': 'sectionheader', 'text': ''}) if configuration.site_enable_sandboxes: output_objects.append({'object_type': 'link', 'destination': 'ssslogin.py', 'class': 'adminlink', 'title': 'Administrate and monitor your sandbox resources', 'text': 'Administrate %s sandbox resources' % \ configuration.short_title, }) output_objects.append({'object_type': 'sectionheader', 'text': ''}) output_objects.append({'object_type': 'link', 'destination': 'oneclick.py', 'class': 'sandboxlink', 'title': 'Run a One-click resource in your browser', 'text': 'Use this computer as One-click %s resource' % \ configuration.short_title, }) return (output_objects, status)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) output_objects.append({'object_type': 'header', 'text': 'Virtual Machines'}) status = returnvalues.OK defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) machine_name = accepted['machine_name'][-1].strip() memory = int(accepted['memory'][-1]) disk = int(accepted['disk'][-1]) vgrid = [name.strip() for name in accepted['vgrid']] architecture = accepted['architecture'][-1].strip() cpu_count = int(accepted['cpu_count'][-1]) cpu_time = int(accepted['cpu_time'][-1]) op_sys = accepted['os'][-1].strip() flavor = accepted['flavor'][-1].strip() hypervisor_re = accepted['hypervisor_re'][-1].strip() sys_re = accepted['sys_re'][-1].strip() action = accepted['action'][-1].strip() title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Virtual Machines' # jquery support for tablesorter and confirmation on "leave": css_helpers = {'css_base': os.path.join(configuration.site_images, 'css'), 'skin_base': configuration.site_skin_base} title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 500, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); $(".vm-tabs").tabs(); } ); </script> ''' if not configuration.site_enable_vmachines: output_objects.append({'object_type': 'text', 'text': '''Virtual machines are disabled on this site. Please contact the Grid admins %s if you think they should be enabled. ''' % configuration.admin_email}) return (output_objects, returnvalues.OK) machine_req = {'memory': memory, 'disk': disk, 'cpu_count': cpu_count, 'cpu_time': cpu_time, 'architecture': architecture, 'vgrid': vgrid, 'os': op_sys, 'flavor': flavor, 'hypervisor_re': hypervisor_re, 'sys_re': sys_re} menu_items = ['vmrequest'] # Html fragments submenu = render_menu(configuration, menu_class='navsubmenu', base_menu=[], user_menu=menu_items) welcome_text = 'Welcome to your %s virtual machine management!' % \ configuration.short_title desc_text = '''On this page you can: <ul> <li>Request Virtual Machines, by clicking on the button above</li> <li>See your virtual machines in the list below.</li> <li>Start, and connect to your Virtual Machine by clicking on it.</li> <li>Edit or delete your Virtual Machine from the Advanced tab.</li> </ul> ''' output_objects.append({'object_type': 'html_form', 'text':''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea> </div> '''}) output_objects.append({'object_type': 'html_form', 'text': submenu}) output_objects.append({'object_type': 'html_form', 'text' : '<p> </p>'}) output_objects.append({'object_type': 'sectionheader', 'text' : welcome_text}) output_objects.append({'object_type': 'html_form', 'text' : desc_text}) user_vms = vms.vms_list(client_id, configuration) if action == 'create': if not configuration.site_enable_vmachines: output_objects.append( {'object_type': 'error_text', 'text': "Virtual machines are disabled on this server"}) status = returnvalues.CLIENT_ERROR return (output_objects, status) if not machine_name: output_objects.append( {'object_type': 'error_text', 'text': "requested build without machine name"}) status = returnvalues.CLIENT_ERROR return (output_objects, status) elif machine_name in [vm["name"] for vm in user_vms]: output_objects.append( {'object_type': 'error_text', 'text': "requested machine name '%s' already exists!" % machine_name}) status = returnvalues.CLIENT_ERROR return (output_objects, status) elif not flavor in vms.available_flavor_list(configuration): output_objects.append( {'object_type': 'error_text', 'text': "requested pre-built flavor not available: %s" % flavor}) status = returnvalues.CLIENT_ERROR return (output_objects, status) elif not hypervisor_re in \ vms.available_hypervisor_re_list(configuration): output_objects.append( {'object_type': 'error_text', 'text': "requested hypervisor runtime env not available: %s" % \ hypervisor_re}) elif not sys_re in vms.available_sys_re_list(configuration): output_objects.append( {'object_type': 'error_text', 'text': "requested system pack runtime env not available: %s" % \ sys_re}) status = returnvalues.CLIENT_ERROR return (output_objects, status) # TODO: support custom build of machine using shared/vmbuilder.py # request for existing pre-built machine vms.create_vm(client_id, configuration, machine_name, machine_req) (action_status, action_msg, job_id) = (True, '', None) if action in ['start', 'stop', 'edit', 'delete']: if not configuration.site_enable_vmachines: output_objects.append( {'object_type': 'error_text', 'text': "Virtual machines are disabled on this server"}) status = returnvalues.CLIENT_ERROR return (output_objects, status) if action == 'start': machine = {} for entry in user_vms: if machine_name == entry['name']: for name in machine_req.keys(): if isinstance(entry[name], basestring) and \ entry[name].isdigit(): machine[name] = int(entry[name]) else: machine[name] = entry[name] break (action_status, action_msg, job_id) = \ vms.enqueue_vm(client_id, configuration, machine_name, machine) elif action == 'edit': if not machine_name in [vm['name'] for vm in user_vms]: output_objects.append( {'object_type': 'error_text', 'text': "No such virtual machine: %s" % machine_name}) status = returnvalues.CLIENT_ERROR return (output_objects, status) (action_status, action_msg) = \ vms.edit_vm(client_id, configuration, machine_name, machine_req) elif action == 'delete': if not machine_name in [vm['name'] for vm in user_vms]: output_objects.append( {'object_type': 'error_text', 'text': "No such virtual machine: %s" % machine_name}) status = returnvalues.CLIENT_ERROR return (output_objects, status) (action_status, action_msg) = \ vms.delete_vm(client_id, configuration, machine_name) elif action == 'stop': # TODO: manage stop - use live I/O to create vmname.stop in job dir pass if not action_status: output_objects.append({'object_type': 'error_text', 'text': action_msg}) # List the machines here output_objects.append({'object_type': 'sectionheader', 'text' : 'Your machines:'}) # Grab the vms available for the user machines = vms.vms_list(client_id, configuration) # Visual representation mapping of the machine state machine_states = { 'EXECUTING': 'vm_running.jpg', 'CANCELED': 'vm_off.jpg', 'FAILED': 'vm_off.jpg', 'FINISHED': 'vm_off.jpg', 'UNKNOWN': 'vm_off.jpg', 'QUEUED': 'vm_booting.jpg', 'PARSE': 'vm_booting.jpg', } # Empirical upper bound on boot time in seconds used to decide between # desktop init and ready states boot_secs = 130 # CANCELED/FAILED/FINISHED -> Powered Off # QUEUED -> Booting if len(machines) > 0: # Create a pretty list with start/edit/stop/connect links pretty_machines = \ '<table style="border: 0; background: none;"><tr>' side_by_side = 3 # How many machines should be shown in a row? col = 0 for machine in machines: # Machines on a row if col % side_by_side == 0: pretty_machines += '</tr><tr>' col += 1 # Html format machine specifications in a fieldset password = '******' exec_time = 0 if machine['job_id'] != 'UNKNOWN' and \ machine['status'] == 'EXECUTING': # TODO: improve on this time selection... # ... in distributed there is no global clock! exec_time = time.time() - 3600 \ - time.mktime(machine['execution_time']) password = vms.vnc_jobid(machine['job_id']) machine_specs = {} machine_specs.update(machine) machine_specs['password'] = password show_specs = """<fieldset> <legend>VM Specs:</legend><ul class="no-bullets"> <li><input type="text" readonly value="%(os)s"> base system</li> <li><input type="text" readonly value="%(flavor)s"> software flavor</li> <li><input type="text" readonly value="%(memory)s"> MB memory</li> <li><input type="text" readonly value="%(disk)s"> GB disk</li> <li><input type="text" readonly value="%(cpu_count)s"> CPU's</li> <li><input type="text" readonly value="%(vm_arch)s"> architecture</li> """ if password != 'UNKNOWN': show_specs += """ <li><input type="text" readonly value="%(password)s"> as VNC password</li> """ show_specs += """ </form></ul></fieldset>""" edit_specs = """<fieldset> <legend>Edit VM Specs:</legend><ul class="no-bullets"> <form method="post" action="vmachines.py"> <input type="hidden" name="action" value="edit"> <input type="hidden" name="machine_name" value="%(name)s"> <input type="hidden" name="output_format" value="html"> <li><input type="text" readonly name="os" value="%(os)s"> base system</li> <li><input type="text" readonly name="flavor" value="%(flavor)s"> software flavor</li> <li><input type="text" readonly name="hypervisor_re" value="%(hypervisor_re)s"> hypervisor runtime env</li> <li><input type="text" readonly name="sys_re" value="%(sys_re)s"> image pack runtime env</li> <li><input type="text" name="memory" value="%(memory)s"> MB memory</li> <li><input type="text" name="disk" value="%(disk)s"> GB disk</li> <li><input type="text" name="cpu_count" value="%(cpu_count)s"> CPU's</li> <li><select name="architecture"> """ for arch in [''] + configuration.architectures: select = '' if arch == machine_specs['architecture']: select = 'selected' edit_specs += "<option %s value='%s'>%s</option>" % (select, arch, arch) edit_specs += """</select> resource architecture <li><input type="text" name="cpu_time" value="%(cpu_time)s"> s time slot</li> <li><select name="vgrid" multiple>""" for vgrid_name in [any_vgrid] + \ user_allowed_vgrids(configuration, client_id): select = '' if vgrid_name in machine_specs['vgrid']: select = 'selected' edit_specs += "<option %s>%s</option>" % (select, vgrid_name) edit_specs += """</select> %s(s)</li>""" % \ configuration.site_vgrid_label if password != 'UNKNOWN': edit_specs += """ <li><input type="text" readonly value="%(password)s"> as VNC password</li> """ edit_specs += """ <input class="styled_button" type="submit" value="Save Changes"> </form>""" js_name = 'deletevm%s' % hexlify("%(name)s" % machine_specs) helper = html_post_helper(js_name, 'vmachines.py', {'machine_name': machine_specs['name'], 'action': 'delete'}) edit_specs += helper edit_specs += """<input class="styled_button" type="submit" value="Delete Machine" onClick="javascript: confirmDialog(%s, '%s');" > """ % (js_name, "Really permanently delete %(name)s VM?" % machine_specs) edit_specs += """</ul></fieldset>""" if machine['status'] == 'EXECUTING' and exec_time > boot_secs: machine_image = '<img src="/images/vms/' \ + machine_states[machine['status']] + '">' elif machine['status'] == 'EXECUTING' and exec_time < boot_secs: machine_image = \ '<img src="/images/vms/vm_desktop_loading.jpg' \ + '">' else: machine_image = '<img src="/images/vms/' \ + machine_states[machine['status']] + '">' machine_link = vms.machine_link(machine_image, machine['job_id'], machine['name'], machine['uuid' ], machine['status'], machine_req) # Smack all the html together fill_dict = {} fill_dict.update(machine) fill_dict['link'] = machine_link fill_dict['show_specs'] = show_specs % machine_specs fill_dict['edit_specs'] = edit_specs % machine_specs pretty_machines += ''' <td style="vertical-align: top;"> <fieldset><legend>%(name)s</legend> <div id="%(name)s-tabs" class="vm-tabs"> <ul> <li><a href="#%(name)s-overview">Overview</a></li> <li><a href="#%(name)s-edit">Advanced</a></li> </ul> <div id="%(name)s-overview"> <p>%(link)s</p> %(show_specs)s </div> <div id="%(name)s-edit"> %(edit_specs)s </div> </div> </fieldset> </td>''' % fill_dict pretty_machines += '</tr></table>' output_objects.append({'object_type': 'html_form', 'text' : pretty_machines}) else: output_objects.append( {'object_type': 'text', 'text' : "You don't have any virtual machines! " "Click 'Request Virtual Machine' to become a proud owner :)" }) return (output_objects, status)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Frozen Archives' # jquery support for tablesorter and confirmation on "leave": title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"> </script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 500, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); // table initially sorted by col. 0 (ID) var sortOrder = [[0,1]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#frozenarchivetable").tablesorter({widgets: ["zebra", "saveSort"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> ''' % default_pager_entries output_objects.append({'object_type': 'header', 'text' : 'Frozen Archives'}) output_objects.append({'object_type': 'html_form', 'text':''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea> </div> ''' }) if not configuration.site_enable_freeze: output_objects.append({'object_type': 'text', 'text': '''Freezing archives is disabled on this site. Please contact the Grid admins %s if you think it should be enabled. ''' % configuration.admin_email}) return (output_objects, returnvalues.OK) output_objects.append( {'object_type': 'text', 'text' : '''Frozen archives are write-once collections of files used e.g. in relation to conference paper submissions. Please note that local policies may prevent users from deleting frozen archives without explicit acceptance from the management. '''}) output_objects.append({'object_type': 'sectionheader', 'text' : 'Existing frozen archives'}) (status, ret) = list_frozen_archives(configuration, client_id) if not status: logger.error("%s: failed for '%s': %s" % (op_name, client_id, ret)) output_objects.append({'object_type': 'error_text', 'text' : ret}) return (output_objects, returnvalues.SYSTEM_ERROR) frozenarchives = [] for freeze_id in ret: (load_status, freeze_dict) = get_frozen_archive(freeze_id, configuration) if not load_status: logger.error("%s: load failed for '%s': %s" % \ (op_name, freeze_id, freeze_dict)) output_objects.append({'object_type': 'error_text', 'text' : 'Could not read details for "%s"' % \ freeze_id}) return (output_objects, returnvalues.SYSTEM_ERROR) freeze_item = build_freezeitem_object(configuration, freeze_dict) freeze_id = freeze_item['id'] flavor = freeze_item.get('flavor', 'freeze') freeze_item['viewfreezelink'] = { 'object_type': 'link', 'destination': "showfreeze.py?freeze_id=%s;flavor=%s" % \ (freeze_id, flavor), 'class': 'infolink', 'title': 'View frozen archive %s' % freeze_id, 'text': ''} if client_id == freeze_item['creator']: js_name = 'delete%s' % hexlify(freeze_id) helper = html_post_helper(js_name, 'deletefreeze.py', {'freeze_id': freeze_id, 'flavor': flavor}) output_objects.append({'object_type': 'html_form', 'text': helper}) freeze_item['delfreezelink'] = { 'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s');" % \ (js_name, 'Really remove %s?' % freeze_id), 'class': 'removelink', 'title': 'Remove %s' % \ freeze_id, 'text': ''} frozenarchives.append(freeze_item) output_objects.append({'object_type': 'table_pager', 'entry_name': 'frozen archives', 'default_entries': default_pager_entries}) output_objects.append({'object_type': 'frozenarchives', 'frozenarchives': frozenarchives}) output_objects.append({'object_type': 'sectionheader', 'text': 'Additional Frozen Archives'}) output_objects.append({'object_type': 'link', 'destination': 'adminfreeze.py', 'class': 'addlink', 'title': 'Specify a new frozen archive', 'text': 'Create a new frozen archive'}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) status = returnvalues.OK defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) (stat, vgrid_list) = vgrid_list_vgrids(configuration) if not stat: output_objects.append({'object_type': 'error_text', 'text' : 'Error getting list of %s.' % \ configuration.site_vgrid_label}) # Check if user wants advanced VGrid component links settings = load_settings(client_id, configuration) collaboration_links = settings.get('SITE_COLLABORATION_LINKS', 'default') if not collaboration_links in configuration.site_collaboration_links or \ collaboration_links == 'default': active_vgrid_links = configuration.site_default_vgrid_links elif collaboration_links == 'advanced': active_vgrid_links = configuration.site_advanced_vgrid_links # Iterate through vgrids and print details for each member_list = {'object_type': 'vgrid_list', 'vgrids': [], 'components': active_vgrid_links} if 'monitor' in active_vgrid_links: vgrid_list = [all_vgrids] + vgrid_list else: vgrid_list.remove(default_vgrid) for vgrid_name in vgrid_list: vgrid_obj = {'object_type': 'vgrid', 'name': vgrid_name} if vgrid_name == default_vgrid: # Everybody is member and allowed to see statistics, Noone # can own it or leave it. Do not add any page links. vgrid_obj['privatemonitorlink'] = {'object_type': 'link', 'destination': 'showvgridmonitor.py?vgrid_name=%s'\ % vgrid_name, 'class': 'monitorlink', 'title': 'View %s monitor' % vgrid_name, 'text': 'View'} vgrid_obj['memberlink'] = {'object_type': 'link', 'destination':'', 'class': 'infolink', 'title': 'Every user is member of the %s %s' \ % (default_vgrid, configuration.site_vgrid_label), 'text': ''} vgrid_obj['administratelink'] = {'object_type': 'link', 'destination':'', 'class': 'infolink', 'title': 'Nobody owns the %s %s' \ % (default_vgrid, configuration.site_vgrid_label), 'text': ''} member_list['vgrids'].append(vgrid_obj) continue elif vgrid_name == all_vgrids: # Only show global monitor link for all_vgrids, Noone # can own it or leave it. Do not add any page links. vgrid_obj['privatemonitorlink'] = {'object_type': 'link', 'destination': 'showvgridmonitor.py?vgrid_name=%s'\ % vgrid_name, 'class': 'monitorlink', 'title': 'View global monitor', 'text': 'View'} vgrid_obj['memberlink'] = {'object_type': 'link', 'destination':'', 'class': 'infolink', 'title': 'Not a real %s - only for global monitor' % \ configuration.site_vgrid_label, 'text': ''} vgrid_obj['administratelink'] = {'object_type': 'link', 'destination':'', 'class': 'infolink', 'title': 'Not a real %s - only for global monitor' % \ configuration.site_vgrid_label, 'text': ''} member_list['vgrids'].append(vgrid_obj) continue # links for everyone: public pages and membership request vgrid_obj['publicscmlink'] = {'object_type': 'link', 'destination': '%s/vgridpublicscm/%s'\ % (configuration.migserver_http_url, vgrid_name), 'class': 'scmlink public', 'title': 'Open %s public SCM' % \ vgrid_name, 'text': 'Open'} vgrid_obj['publictrackerlink'] = {'object_type': 'link', 'destination': '%s/vgridpublictracker/%s'\ % (configuration.migserver_http_url, vgrid_name), 'class': 'trackerlink public', 'title': 'Open %s public tracker' % \ vgrid_name, 'text': 'Open'} vgrid_obj['enterpubliclink'] = {'object_type': 'link', 'destination': '%s/vgrid/%s/path/index.html' % \ (configuration.migserver_http_url, vgrid_name), 'class': 'urllink member', 'title': 'View public %s web page' % \ vgrid_name, 'text': 'View'} # link to become member: overwritten later for members js_name = 'reqvgridmember%s' % hexlify(vgrid_name) helper = html_post_helper(js_name, 'sendrequestaction.py', {'vgrid_name': vgrid_name, 'request_type': 'vgridmember', 'request_text': ''}) output_objects.append({'object_type': 'html_form', 'text': helper}) vgrid_obj['memberlink'] = \ {'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s', '%s');"\ % (js_name, "Request membership of " + \ vgrid_name + ":<br/>" + \ "\nPlease write a message to the owners (field below).", 'request_text'), 'class': 'addlink', 'title': 'Request membership of %s' % \ vgrid_name, 'text': ''} # link to become owner: overwritten later for owners js_name = 'reqvgridowner%s' % hexlify(vgrid_name) helper = html_post_helper(js_name, 'sendrequestaction.py', {'vgrid_name': vgrid_name, 'request_type': 'vgridowner', 'request_text': ''}) output_objects.append({'object_type': 'html_form', 'text': helper}) vgrid_obj['administratelink'] = \ {'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s', '%s');"\ % (js_name, "Request ownership of " + \ vgrid_name + ":<br/>" + \ "\nPlease write a message to the owners (field below).", 'request_text'), 'class': 'addadminlink', 'title': 'Request ownership of %s' % \ vgrid_name, 'text': ''} # members/owners are allowed to view private pages and monitor if vgrid_is_owner_or_member(vgrid_name, client_id, configuration): vgrid_obj['enterprivatelink'] = {'object_type': 'link', 'destination': '../vgrid/%s/path/index.html' % \ vgrid_name, 'class': 'urllink owner', 'title': 'View private %s web page' % \ vgrid_name, 'text': 'View'} vgrid_obj['sharedfolderlink'] = {'object_type': 'link', 'destination': 'fileman.py?path=%s/' % vgrid_name, 'class': 'sharedfolderlink', 'title': 'Open shared %s folder' \ % vgrid_name, 'text': 'Open'} vgrid_obj['memberscmlink'] = {'object_type': 'link', 'destination': '/vgridscm/%s' % \ vgrid_name, 'class': 'scmlink member', 'title': 'View %s members scm' % \ vgrid_name, 'text': 'View'} vgrid_obj['membertrackerlink'] = {'object_type': 'link', 'destination': '/vgridtracker/%s' % \ vgrid_name, 'class': 'trackerlink member', 'title': 'View %s members tracker' % \ vgrid_name, 'text': 'View'} vgrid_obj['privateforumlink'] = {'object_type': 'link', 'destination': 'vgridforum.py?vgrid_name=%s' % \ vgrid_name, 'class': 'forumlink', 'title': 'Open %s private forum' \ % vgrid_name, 'text': 'Open'} vgrid_obj['privateworkflowslink'] = {'object_type': 'link', 'destination': 'vgridworkflows.py?vgrid_name=%s' % \ vgrid_name, 'class': 'workflowslink', 'title': 'Open %s private workflows' \ % vgrid_name, 'text': 'Open'} vgrid_obj['privatemonitorlink'] = {'object_type': 'link', 'destination': 'showvgridmonitor.py?vgrid_name=%s'\ % vgrid_name, 'class': 'monitorlink', 'title': 'View %s monitor' % \ vgrid_name, 'text': 'View'} # to leave this VGrid (remove ourselves). Note that we are # going to overwrite the link later for owners. js_name = 'rmvgridmember%s' % hexlify(vgrid_name) helper = html_post_helper(js_name, 'rmvgridmember.py', {'vgrid_name': vgrid_name, 'cert_id': client_id}) output_objects.append({'object_type': 'html_form', 'text': helper}) vgrid_obj['memberlink'] = \ {'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s');"\ % (js_name, "Really leave " + vgrid_name + "?"), 'class': 'removelink', 'title': 'Leave %s members' % vgrid_name, 'text': ''} # owners are allowed to edit pages and administrate if vgrid_is_owner(vgrid_name, client_id, configuration): vgrid_obj['ownerscmlink'] = {'object_type': 'link', 'destination': '/vgridownerscm/%s' % \ vgrid_name, 'class': 'scmlink owner', 'title': 'View %s owners scm' % \ vgrid_name, 'text': 'View'} vgrid_obj['ownertrackerlink'] = {'object_type': 'link', 'destination': '/vgridownertracker/%s' % \ vgrid_name, 'class': 'trackerlink owner', 'title': 'View %s owners tracker' % \ vgrid_name, 'text': 'View'} # correct the link to leave the VGrid js_name = 'rmvgridowner%s' % hexlify(vgrid_name) helper = html_post_helper(js_name, 'rmvgridowner.py', {'vgrid_name': vgrid_name, 'cert_id': client_id}) output_objects.append({'object_type': 'html_form', 'text': helper}) vgrid_obj['memberlink']['destination'] = \ "javascript: confirmDialog(%s,'%s');" % \ (js_name, "Really leave " + vgrid_name + "?") vgrid_obj['memberlink']['class'] = 'removeadminlink' vgrid_obj['memberlink']['title'] = 'Leave %s owners' % vgrid_name # add more links: administrate and edit pages vgrid_obj['administratelink'] = {'object_type': 'link', 'destination': 'adminvgrid.py?vgrid_name=%s'\ % vgrid_name, 'class': 'adminlink', 'title': 'Administrate %s' % vgrid_name, 'text': ''} vgrid_obj['editprivatelink'] = {'object_type': 'link', 'destination': 'fileman.py?path=private_base/%s/'\ % vgrid_name, 'class': 'editlink owner', 'title': 'Edit private %s web page' % vgrid_name, 'text': 'Edit'} vgrid_obj['editpubliclink'] = {'object_type': 'link', 'destination': 'fileman.py?path=public_base/%s/'\ % vgrid_name, 'class': 'editlink member', 'title': 'Edit public %s web page' % vgrid_name, 'text': 'Edit'} member_list['vgrids'].append(vgrid_obj) title_entry = find_entry(output_objects, 'title') label = "%ss" % configuration.site_vgrid_label # Append VGrid note if custom if label != 'VGrid': label += ' (i.e. VGrids)' title_entry['text'] = '%s administration' % label # jquery support for tablesorter and confirmation on "leave": title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 500, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); // table initially sorted by col. 1 (admin), then 2 (member), then 0 (name) var sortOrder = [[1,1],[2,1],[0,0]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#vgridtable").tablesorter({widgets: ["zebra", "saveSort"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> ''' % default_pager_entries output_objects.append({'object_type': 'html_form', 'text':''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea> </div> ''' }) output_objects.append({'object_type': 'header', 'text': label}) output_objects.append({'object_type': 'text', 'text' : ''' %ss share files, a number of collaboration tools and resources. Members can access web pages, files, tools and resources. Owners can additionally edit pages, as well as add and remove members or resources. ''' % configuration.site_vgrid_label }) if configuration.site_vgrid_label != 'VGrid': output_objects.append({'object_type': 'text', 'text' : """Please note that for historical reasons %ss are also referred to as VGrids in some contexts.""" % \ configuration.site_vgrid_label}) output_objects.append({'object_type': 'sectionheader', 'text' : '%ss managed on this server' % \ configuration.site_vgrid_label}) output_objects.append({'object_type': 'table_pager', 'entry_name': '%ss' % \ configuration.site_vgrid_label, 'default_entries': default_pager_entries}) output_objects.append(member_list) user_map = get_full_user_map(configuration) user_dict = user_map.get(client_id, None) # Optional limitation of create vgrid permission if user_dict and vgrid_create_allowed(configuration, user_dict): output_objects.append({'object_type': 'sectionheader', 'text' : 'Additional %ss' % \ configuration.site_vgrid_label}) output_objects.append( {'object_type': 'text', 'text': '''Please enter a name for the new %(label)s to add, using slashes to specify nesting. I.e. if you own a %(label)s called ABC, you can create a sub-%(label)s called DEF by entering ABC/DEF below.''' % \ {'label': configuration.site_vgrid_label}}) output_objects.append({'object_type': 'html_form', 'text': '''<form method="post" action="createvgrid.py"> <input type="text" size=40 name="vgrid_name" /> <input type="hidden" name="output_format" value="html" /> <input type="submit" value="Create %s" /> </form> ''' % configuration.site_vgrid_label}) output_objects.append({'object_type': 'sectionheader', 'text' : 'Request Access to %ss' % \ configuration.site_vgrid_label}) output_objects.append( {'object_type': 'text', 'text': '''You can request access to %(label)ss using the individual plus-icons above directly or by entering the name of the %(label)s to request access to, what kind of access and an optional message to the admins below''' % \ {'label': configuration.site_vgrid_label}}) output_objects.append({'object_type': 'html_form', 'text': '''<form method="post" action="sendrequestaction.py"> <input type="text" size=40 name="vgrid_name" /> <select name="request_type"> <option value="vgridmember">membership</option> <option value="vgridowner">ownership</option> </select> <input type="text" size=50 name="request_text" /> <input type="hidden" name="output_format" value="html" /> <input type="submit" value="Request %s access" /> </form> ''' % configuration.site_vgrid_label}) return (output_objects, status)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) client_dir = client_id_dir(client_id) status = returnvalues.OK defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) show_description = accepted['description'][-1].lower() == 'true' # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath(os.path.join(configuration.user_home, client_dir)) + os.sep template_path = os.path.join(base_dir, default_mrsl_filename) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Submit Job' output_objects.append({'object_type': 'header', 'text' : 'Submit Job'}) default_mrsl = get_default_mrsl(template_path) settings_dict = load_settings(client_id, configuration) if not settings_dict or not settings_dict.has_key('SUBMITUI'): logger.info('Settings dict does not have SUBMITUI key - using default' ) submit_style = configuration.submitui[0] else: submit_style = settings_dict['SUBMITUI'] # We generate all 3 variants of job submission (fields, textarea, files), # initially hide them and allow to switch between them using js. # could instead extract valid prefixes as in settings.py # (means: by "eval" from configuration). We stick to hard-coding. submit_options = ['fields_form', 'textarea_form', 'files_form'] css_helpers = {'css_base': os.path.join(configuration.site_images, 'css'), 'skin_base': configuration.site_skin_base} title_entry['style'] = themed_styles(configuration, base=['jquery.fileupload.css', 'jquery.fileupload-ui.css'], skin=['fileupload-ui.custom.css']) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <!-- Filemanager is only needed for fancy upload init wrapper --> <script type="text/javascript" src="/images/js/jquery.form.js"></script> <script type="text/javascript" src="/images/js/jquery.filemanager.js"></script> <!-- Fancy file uploader and dependencies --> <!-- The Templates plugin is included to render the upload/download listings --> <script type="text/javascript" src="/images/js/tmpl.min.js"></script> <!-- The Load Image plugin is included for the preview images and image resizing functionality --> <script type="text/javascript" src="/images/js/load-image.min.js"></script> <!-- The Iframe Transport is required for browsers without support for XHR file uploads --> <script type="text/javascript" src="/images/js/jquery.iframe-transport.js"></script> <!-- The basic File Upload plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload.js"></script> <!-- The File Upload processing plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-process.js"></script> <!-- The File Upload image preview & resize plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-image.js"></script> <!-- The File Upload validation plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-validate.js"></script> <!-- The File Upload user interface plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-ui.js"></script> <!-- The File Upload jQuery UI plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-jquery-ui.js"></script> <script type="text/javascript" > options = %s; function setDisplay(this_id, new_d) { //console.log("setDisplay with: "+this_id); el = document.getElementById(this_id) if ( el == undefined || el.style == undefined ) { console.log("failed to locate display element: "+this_id); return; // avoid js null ref errors } el.style.display=new_d; } function switchTo(name) { //console.log("switchTo: "+name); for (o=0; o < options.length; o++) { if (name == options[o]) { setDisplay(options[o],"block"); } else { setDisplay(options[o],"none"); } } } function openFancyUpload() { var open_dialog = mig_fancyuploadchunked_init("fancyuploadchunked_dialog"); var remote_path = "."; open_dialog("Upload Files", function() { return false; }, remote_path, false); } $(document).ready( function() { //console.log("document ready handler"); switchTo("%s"); $("#fancydialog").click(openFancyUpload); }); </script> ''' % (submit_options, submit_style + "_form") output_objects.append({'object_type': 'text', 'text': 'This page is used to submit jobs to the grid.'}) output_objects.append({'object_type': 'verbatim', 'text': ''' There are %s interface styles available that you can choose among:''' % \ len(submit_options)}) links = [] for opt in submit_options: name = opt.split('_', 2)[0] links.append({'object_type': 'link', 'destination': "javascript:switchTo('%s')" % opt, 'class': 'submit%slink' % name, 'title': 'Switch to %s submit interface' % name, 'text' : '%s style' % name, }) output_objects.append({'object_type': 'multilinkline', 'links': links}) output_objects.append({'object_type': 'text', 'text': ''' Please note that changes to the job description are *not* automatically transferred if you switch style.'''}) output_objects.append({'object_type': 'html_form', 'text': '<div id="fields_form" style="display:none;">\n'}) # Fields output_objects.append({'object_type': 'sectionheader', 'text' : 'Please fill in your job description in the fields' ' below:' }) output_objects.append({'object_type': 'text', 'text' : """ Please fill in one or more fields below to define your job before hitting Submit Job at the bottom of the page. Empty fields will simply result in the default value being used and each field is accompanied by a help link providing further details about the field."""}) output_objects.append({'object_type': 'html_form', 'text' : """ <div class="submitjob"> <form method="post" action="submitfields.py"> """ }) show_fields = get_job_specs(configuration) try: parsed_mrsl = dict(parse_lines(default_mrsl)) except: parsed_mrsl = {} # Find allowed VGrids and Runtimeenvironments and add them to # configuration object for automated choice handling allowed_vgrids = user_allowed_vgrids(configuration, client_id) + \ [any_vgrid] allowed_vgrids.sort() configuration.vgrids = allowed_vgrids (re_status, allowed_run_envs) = list_runtime_environments(configuration) if not re_status: logger.error('Failed to extract allowed runtime envs: %s' % \ allowed_run_envs) allowed_run_envs = [] allowed_run_envs.sort() configuration.runtimeenvironments = allowed_run_envs user_res = user_allowed_res_exes(configuration, client_id) # Allow any exe unit on all allowed resources allowed_resources = ['%s_*' % res for res in user_res.keys()] allowed_resources.sort() configuration.resources = allowed_resources field_size = 30 area_cols = 80 area_rows = 5 for (field, spec) in show_fields: title = spec['Title'] if show_description: description = '%s<br />' % spec['Description'] else: description = '' field_type = spec['Type'] # Use saved value and fall back to default if it is missing saved = parsed_mrsl.get('::%s::' % field, None) if saved: if not spec['Type'].startswith('multiple'): default = saved[0] else: default = saved else: default = spec['Value'] # Hide sandbox field if sandboxes are disabled if field == 'SANDBOX' and not configuration.site_enable_sandboxes: continue if 'invisible' == spec['Editor']: continue if 'custom' == spec['Editor']: continue output_objects.append({'object_type': 'html_form', 'text' : """ <b>%s:</b> <a class='infolink' href='docs.py?show=job#%s'>help</a><br /> %s""" % (title, field, description) }) if 'input' == spec['Editor']: if field_type.startswith('multiple'): output_objects.append({'object_type': 'html_form', 'text' : """ <textarea name='%s' cols='%d' rows='%d'>%s</textarea><br /> """ % (field, area_cols, area_rows, '\n'.join(default)) }) else: output_objects.append({'object_type': 'html_form', 'text' : """ <input type='text' name='%s' size='%d' value='%s' /><br /> """ % (field, field_size, default) }) elif 'select' == spec['Editor']: choices = available_choices(configuration, client_id, field, spec) res_value = default value_select = '' if field_type.startswith('multiple'): value_select += '<div class="scrollselect">' for name in choices: # Blank default value does not make sense here if not str(name): continue selected = '' if str(name) in res_value: selected = 'checked' value_select += ''' <input type="checkbox" name="%s" %s value=%s>%s<br /> ''' % (field, selected, name, name) value_select += '</div>\n' else: value_select += "<select name='%s'>\n" % field for name in choices: selected = '' if str(res_value) == str(name): selected = 'selected' display = "%s" % name if display == '': display = ' ' value_select += """<option %s value='%s'>%s</option>\n""" \ % (selected, name, display) value_select += """</select><br />\n""" output_objects.append({'object_type': 'html_form', 'text' : value_select }) output_objects.append({'object_type': 'html_form', 'text': "<br />"}) output_objects.append({'object_type': 'html_form', 'text' : """ <br /> <table class='centertext'> <tr><td><input type='submit' value='Submit Job' /> <input type='checkbox' name='save_as_default'> Save as default job template </td></tr> </table> <br /> </form> </div> """ }) output_objects.append({'object_type': 'html_form', 'text': ''' </div><!-- fields_form--> <div id="textarea_form" style="display:none;"> '''}) # Textarea output_objects.append({'object_type': 'sectionheader', 'text' : 'Please enter your mRSL job description below:' }) output_objects.append({'object_type': 'html_form', 'text' : """ <div class='smallcontent'> Job descriptions can use a wide range of keywords to specify job requirements and actions.<br /> Each keyword accepts one or more values of a particular type.<br /> The full list of keywords with their default values and format is available in the on-demand <a href='docs.py?show=job'>mRSL Documentation</a>. <p> Actual examples for inspiration: <a href=/public/cpuinfo.mRSL>CPU Info</a>, <a href=/public/basic-io.mRSL>Basic I/O</a>, <a href=/public/notification.mRSL>Job Notification</a>, <a href=/public/povray.mRSL>Povray</a> and <a href=/public/vcr.mRSL>VCR</a> </div> """}) output_objects.append({'object_type': 'html_form', 'text' : """ <!-- Please note that textarea.py chokes if no nonempty KEYWORD_X_Y_Z fields are supplied: thus we simply send a bogus jobname which does nothing --> <form method="post" action="textarea.py"> <table class="submitjob"> <tr><td class="centertext"> <input type=hidden name=jobname_0_0_0 value=" " /> <textarea cols="82" rows="25" name="mrsltextarea_0"> %(default_mrsl)s </textarea> </td></tr> <tr><td class='centertext'> <input type="submit" value="Submit Job" /> <input type="checkbox" name="save_as_default" >Save as default job template </td></tr> </table> </form> """ % {'default_mrsl': default_mrsl}}) output_objects.append({'object_type': 'html_form', 'text': ''' </div><!-- textarea_form--> <div id="files_form" style="display:none;"> '''}) # Upload form output_objects.append({'object_type': 'sectionheader', 'text' : 'Please upload your job file or packaged job files' ' below:' }) output_objects.append({'object_type': 'html_form', 'text' : """ <form enctype='multipart/form-data' action='textarea.py' method='post'> <table class='files'> <tr class='title'><td class='centertext' colspan=4> Upload job files </td></tr> <tr><td colspan=3> Upload file to current directory (%(dest_dir)s) </td><td><br /></td></tr> <tr><td colspan=2> Extract package files (.zip, .tar.gz, .tar.bz2) </td><td colspan=2> <input type=checkbox name='extract_0' /> </td></tr> <tr><td colspan=2> Submit mRSL files (also .mRSL files included in packages) </td><td colspan=2> <input type=checkbox name='submitmrsl_0' checked /> </td></tr> <tr><td> File to upload </td><td class='righttext' colspan=3> <input name='fileupload_0_0_0' type='file'/> </td></tr> <tr><td> Optional remote filename (extra useful in windows) </td><td class='righttext' colspan=3> <input name='default_remotefilename_0' type='hidden' value='%(dest_dir)s'/> <input name='remotefilename_0' type='text' size='50' value='%(dest_dir)s'/> <input type='submit' value='Upload' name='sendfile'/> </td></tr> </table> </form> <table class='files'> <tr class='title'><td class='centertext'> Upload other files efficiently (using chunking). </td></tr> <tr><td class='centertext'> <button id='fancydialog'>Open Upload dialog</button> </td></tr> </table> </div><!-- files_form--> <div id='fancyuploadchunked_dialog' title='Upload File' style='display: none;'> <!-- The file upload form used as target for the file upload widget --> <form id='fancyfileupload' action='uploadchunked.py?output_format=json;action=put' method='POST' enctype='multipart/form-data'> <fieldset id='fancyfileuploaddestbox'> <label id='fancyfileuploaddestlabel' for='fancyfileuploaddest'> Optional final destination dir: </label> <input id='fancyfileuploaddest' type='text' size=60 value=''> </fieldset> <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload --> <div class='fileupload-buttonbar'> <div class='fileupload-buttons'> <!-- The fileinput-button span is used to style the file input field as button --> <span class='fileinput-button'> <span>Add files...</span> <input type='file' name='files[]' multiple> </span> <button type='submit' class='start'>Start upload</button> <button type='reset' class='cancel'>Cancel upload</button> <button type='button' class='delete'>Delete</button> <input type='checkbox' class='toggle'> <!-- The global file processing state --> <span class='fileupload-process'></span> </div> <!-- The global progress state --> <div class='fileupload-progress fade' style='display:none'> <!-- The global progress bar --> <div class='progress' role='progressbar' aria-valuemin='0' aria-valuemax='100'></div> <!-- The extended global progress state --> <div class='progress-extended'> </div> </div> </div> <!-- The table listing the files available for upload/download --> <table role='presentation' class='table table-striped'><tbody class='uploadfileslist'></tbody></table> </form> <!-- For status and error output messages --> <div id='fancyuploadchunked_output'></div> </div> """ % {'dest_dir': '.' + os.sep}}) output_objects.append({'object_type': 'html_form', 'text': ''' <!-- The template to display files available for upload --> <script id="template-upload" type="text/x-tmpl"> {% console.log("using upload template"); %} {% console.log("... with upload files: "+$.fn.dump(o)); %} {% var dest_dir = "./" + $("#fancyfileuploaddest").val(); %} {% console.log("using upload dest: "+dest_dir); %} {% for (var i=0, file; file=o.files[i]; i++) { %} {% var rel_path = $.fn.normalizePath(dest_dir+"/"+file.name); %} {% console.log("using upload rel_path: "+rel_path); %} <tr class="template-upload fade"> <td> <span class="preview"></span> </td> <td> <p class="name">{%=rel_path%}</p> <strong class="error"></strong> </td> <td> <div class="size pending">Processing...</div> <div class="progress"></div> </td> <td> {% if (!i && !o.options.autoUpload) { %} <button class="start" disabled>Start</button> {% } %} {% if (!i) { %} <button class="cancel">Cancel</button> {% } %} </td> </tr> {% } %} </script> <!-- The template to display files available for download --> <script id="template-download" type="text/x-tmpl"> {% console.log("using download template"); %} {% console.log("... with download files: "+$.fn.dump(o)); %} {% for (var i=0, file; file=o.files[i]; i++) { %} {% var rel_path = $.fn.normalizePath("./"+file.name); %} {% console.log("using download rel_path: "+rel_path); %} <tr class="template-download fade"> <td> <span class="preview"> {% if (file.thumbnailUrl) { %} <a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" data-gallery><img src="{%=file.thumbnailUrl%}"></a> {% } %} </span> </td> <td> <p class="name"> <a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" {%=file.thumbnailUrl?\'data-gallery\':\'\'%}>{%=rel_path%}</a> </p> {% if (file.error) { %} <div><span class="error">Error</span> {%=file.error%}</div> {% } %} </td> <td> <div class="size">{%=o.formatFileSize(file.size)%}</div> </td> <td> <button class="delete" data-type="{%=file.deleteType%}" data-url="{%=file.deleteUrl%}"{% if (file.deleteWithCredentials) { %} data-xhr-fields=\'{"withCredentials":true}\'{% } %}>{% if (file.deleteUrl) { %}Delete{% } else { %}Dismiss{% } %}</button> <input type="checkbox" name="delete" value="1" class="toggle"> </td> </tr> {% } %} </script> '''}) return (output_objects, status)
%s %s %s %s ''' % (cf_init, fu_init, select_all_javascript(), selected_file_actions_javascript()) add_ready = ''' %s %s /* wrap openFancyUpload in function to avoid event data as argument */ $("#%s").click(function() { openFancyUpload(); }); $("#checkall_box").click(toggleChecked); ''' % (cf_ready, fu_ready, open_button_id) # TODO: can we update style inline to avoid explicit themed_styles? styles = themed_styles(configuration, advanced=['jquery.fileupload.css', 'jquery.fileupload-ui.css'], skin=['fileupload-ui.custom.css'], user_settings=user_settings) styles['advanced'] += ''' %s ''' % visibility_toggle title_entry['style'] = styles title_entry['script']['advanced'] += add_import title_entry['script']['init'] += add_init title_entry['script']['ready'] += add_ready title_entry['script']['body'] = ' class="%s"' % main_class output_objects.append({'object_type': 'header', 'text': page_title}) # TODO: move to output html handler output_objects.append({'object_type': 'html_form', 'text': confirm_html(configuration)})
def main(client_id, user_arguments_dict, environ=None): """Main function used by front end""" (configuration, logger, output_objects, op_name) = initialize_main_variables(client_id, op_header=False, op_title=False, op_menu=client_id) # Extract raw data first if environ is None: environ = os.environ request_url = environ.get('REQUEST_URI', '/') user_agent = environ.get('HTTP_USER_AGENT', '') user_addr = environ.get('REMOTE_ADDR', '') user_id = environ.get('REMOTE_USER', '') # IMPORTANT: use all actual args as base and override with real signature all_args = query_args(environ) defaults = signature()[1] all_args.update(defaults) (validate_status, accepted) = validate_input(user_arguments_dict, all_args, output_objects, allow_rejects=False) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) token = accepted['token'][-1] redirect_url = accepted['redirect_url'][-1] check_only = False # logger.debug("User: %s executing %s with redirect url %s" % # (client_id, op_name, redirect_url)) # logger.debug("env: %s" % environ) if not configuration.site_enable_twofactor: output_objects.append({ 'object_type': 'error_text', 'text': '''2FA is not enabled on the system''' }) return (output_objects, returnvalues.SYSTEM_ERROR) if configuration.site_twofactor_strict_address \ and not expire_twofactor_session(configuration, client_id, environ, allow_missing=True, not_user_addr=user_addr): logger.error("could not expire old 2FA sessions for %s" % client_id) output_objects.append({ 'object_type': 'error_text', 'text': "Internal error: could not expire old 2FA sessions!" }) return (output_objects, returnvalues.ERROR) status = returnvalues.OK # check that the user is already authenticated (does state file exist?) # or run through validation of provided time-based one-time password if redirect_url: # Build forward query string from any real non-local args forward_args = {} for (key, val) in accepted.items(): if key not in defaults.keys() and val != ['AllowMe']: forward_args[key] = val redirect_location = redirect_url if forward_args: redirect_location += '?%s' % urllib.urlencode(forward_args, True) # Manual url decoding required for e.g. slashes redirect_location = urllib.unquote(redirect_location) headers = [('Status', '302 Moved'), ('Location', redirect_location)] logger.debug("redirect_url %s and args %s gave %s" % (redirect_url, forward_args, redirect_location)) else: headers = [] twofactor_dict = load_twofactor(client_id, configuration, allow_missing=True) logger.debug("found twofactor_dict for %s : %s" % (client_id, twofactor_dict)) if not twofactor_dict: logger.warning("fall back to twofactor defaults for %s" % client_id) twofactor_dict = dict([ (i, j['Value']) for (i, j) in twofactor_defaults(configuration).items() ]) # NOTE: twofactor_defaults field availability depends on configuration if not redirect_url: # This is the 2FA setup check mode check_only = True require_twofactor = True elif user_id.startswith(configuration.user_mig_oid_provider) and \ twofactor_dict.get('MIG_OID_TWOFACTOR', False): require_twofactor = True elif user_id.startswith(configuration.user_ext_oid_provider) \ and twofactor_dict.get('EXT_OID_TWOFACTOR', False): require_twofactor = True else: require_twofactor = False # Fail gently if twofactor dependencies are unavailable if require_twofactor and not twofactor_available(configuration): logger.error("Required dependencies are missing for 2FA support") require_twofactor = False if require_twofactor: logger.info("detected 2FA requirement for %s on %s" % (client_id, request_url)) b32_secret = None if token: b32_secret = load_twofactor_key(client_id, configuration) if not b32_secret: logger.warning("found no saved 2FA secret for %s" % client_id) output_objects.append({ 'object_type': 'error_text', 'text': "Please contact the %s admins to get your 2FA secret" % configuration.short_title }) return (output_objects, returnvalues.ERROR) # Check that user provided matching token and set cookie on success if token and b32_secret and verify_twofactor_token( configuration, client_id, b32_secret, token): logger.info('Accepted valid auth token from %s' % client_id) else: styles = themed_styles(configuration) output_objects.append({ 'object_type': 'title', 'text': '2-Factor Authentication', 'skipmenu': True, 'style': styles }) output_objects.append({ 'object_type': 'html_form', 'text': twofactor_token_html(configuration) }) if token: logger.warning('Invalid token for %s (%s vs %s) - try again' % (client_id, token, get_twofactor_token(configuration, client_id, b32_secret))) # NOTE: we keep actual result in plain text for json extract output_objects.append({ 'object_type': 'html_form', 'text': ''' <div class="twofactorstatus"> <div class="error leftpad errortext"> ''' }) output_objects.append({ 'object_type': 'text', 'text': 'Incorrect token provided - please try again' }) output_objects.append({ 'object_type': 'html_form', 'text': ''' </div> </div>''' }) # TODO: proper rate limit source / user here? time.sleep(3) return (output_objects, status) else: logger.info("no 2FA requirement for %s on %s" % (client_id, request_url)) # If we get here we either got correct token or verified 2FA to be disabled if check_only: logger.info("skip session init in setup check for %s" % client_id) else: cookie = Cookie.SimpleCookie() # TODO: reuse any existing session? # create a secure session cookie session_key = generate_session_key(configuration, client_id) session_start = time.time() cookie['2FA_Auth'] = session_key cookie['2FA_Auth']['path'] = '/' # NOTE: SimpleCookie translates expires ttl to actual date from now cookie['2FA_Auth']['expires'] = twofactor_cookie_ttl cookie['2FA_Auth']['secure'] = True cookie['2FA_Auth']['httponly'] = True # GDP only allow one active 2FA-session if configuration.site_enable_gdp: if not expire_twofactor_session( configuration, client_id, environ, allow_missing=True): logger.error("could not expire old 2FA sessions for %s" % client_id) output_objects.append({ 'object_type': 'error_text', 'text': "Internal error: could not expire old 2FA sessions!" }) return (output_objects, returnvalues.ERROR) # Create the state file to inform apache (rewrite) about auth # We save user info to be able to monitor and expire active sessions if not save_twofactor_session(configuration, client_id, session_key, user_addr, user_agent, session_start): logger.error("could not create 2FA session for %s" % client_id) output_objects.append({ 'object_type': 'error_text', 'text': "Internal error: could not create 2FA session!" }) return (output_objects, returnvalues.ERROR) logger.info("saved 2FA session for %s in %s" % (client_id, session_key)) if redirect_url: headers.append(tuple(str(cookie).split(': ', 1))) output_objects.append({'object_type': 'start', 'headers': headers}) output_objects.append({'object_type': 'script_status'}) else: output_objects.append({ 'object_type': 'title', 'text': '2FA', 'skipmenu': True }) # NOTE: we keep actual result in plain text for json extract output_objects.append({ 'object_type': 'html_form', 'text': ''' <!-- Keep similar spacing --> <div class="twofactorbg"> <div class="twofactorstatus"> <div class="ok leftpad"> ''' }) output_objects.append({ 'object_type': 'text', 'text': 'Correct token provided!' }) output_objects.append({ 'object_type': 'html_form', 'text': ''' </div> <p> <a href="">Test again</a> or <a href="javascript:close();">close</a> this tab/window and proceed. </p> </div> </div>''' }) # logger.debug("return from %s for %s with headers: %s" % # (op_name, client_id, headers)) return (output_objects, status)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = initialize_main_variables(client_id, op_header=False) client_dir = client_id_dir(client_id) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) title_entry = find_entry(output_objects, "title") title_entry["text"] = "People" # jquery support for confirmation-style popup: title_entry["style"] = themed_styles(configuration) title_entry[ "javascript" ] = """ <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 640, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); } ); </script> """ output_objects.append( { "object_type": "html_form", "text": """ <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="72" rows="10" id="confirm_input" style="display:none;"></textarea> </div> """, } ) user_list = accepted["cert_id"] # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath(os.path.join(configuration.user_home, client_dir)) + os.sep status = returnvalues.OK title_entry = find_entry(output_objects, "title") title_entry["text"] = "User details" output_objects.append({"object_type": "header", "text": "Show user details"}) visible_user = user_visible_user_confs(configuration, client_id) allowed_vgrids = user_allowed_vgrids(configuration, client_id) for visible_user_name in user_list: if not visible_user_name in visible_user.keys(): output_objects.append( {"object_type": "error_text", "text": "invalid user %s (%s)" % (visible_user_name, visible_user)} ) continue user_dict = visible_user[visible_user_name] user_item = build_useritem_object_from_user_dict( configuration, visible_user_name, base_dir, user_dict, allowed_vgrids ) output_objects.append(user_item) return (output_objects, status)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) vgrid_name = accepted['vgrid_name'][-1] action = accepted['action'][-1] thread = accepted['thread'][-1] msg_subject = accepted['msg_subject'][-1].strip() msg_body = accepted['msg_body'][-1].strip() if not vgrid_is_owner_or_member(vgrid_name, client_id, configuration): output_objects.append({'object_type': 'error_text', 'text': '''You must be an owner or member of %s %s to access the forum.''' % (vgrid_name, configuration.site_vgrid_label)}) return (output_objects, returnvalues.CLIENT_ERROR) if not action in valid_actions: output_objects.append({'object_type': 'error_text', 'text' : 'Invalid action "%s" (supported: %s)' % \ (action, ', '.join(valid_actions))}) return (output_objects, returnvalues.CLIENT_ERROR) if action in post_actions and not correct_handler('POST'): output_objects.append( {'object_type': 'error_text', 'text' : 'Only accepting POST requests to prevent unintended updates'}) return (output_objects, returnvalues.CLIENT_ERROR) # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath(os.path.join(configuration.vgrid_private_base, vgrid_name)) + os.sep forum_base = os.path.abspath(os.path.join(base_dir, '.vgridforum')) title_entry = find_entry(output_objects, 'title') title_entry['text'] = '%s Forum' % configuration.site_vgrid_label css_helpers = {'css_base': os.path.join(configuration.site_images, 'css'), 'skin_base': configuration.site_skin_base} title_entry['style'] = themed_styles(configuration, advanced=['forum.css']) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript"> function toggle_new(form_elem_id, link_elem_id) { form_elem = document.getElementById(form_elem_id); form_focus = document.getElementById(form_elem_id + "_main"); link_elem = document.getElementById(link_elem_id); if (!form_elem || !link_elem) return; if (form_elem.style.display != 'block') { form_elem.style.display = 'block'; form_focus.focus(); link_elem.style.display = 'none'; } else { form_elem.style.display = 'none'; link_elem.style.display = 'block'; link_elem.focus(); } } $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 640, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); // table initially sorted by 0 (last update / date) var sortOrder = [[0,1]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#forumtable").tablesorter({widgets: ["zebra"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> ''' % default_pager_entries output_objects.append({'object_type': 'html_form', 'text':''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="72" rows="10" id="confirm_input" style="display:none;"></textarea> </div> ''' }) output_objects.append({'object_type': 'sectionheader', 'text' : '%s Forum for %s' % \ (configuration.site_vgrid_label, vgrid_name)}) try: os.makedirs(forum_base) except: pass if not os.path.isdir(forum_base): output_objects.append({'object_type': 'error_text', 'text': '''No forum available for %s!''' % vgrid_name}) return (output_objects, returnvalues.SYSTEM_ERROR) post_error = None msg = '' logger.info("vgridforum %s %s %s" % (vgrid_name, action, thread)) if action in post_actions: if action == 'new_thread': try: (thread_hash, msg) = new_subject(forum_base, client_id, msg_subject, msg_body) query = 'vgrid_name=%s&action=show_thread&thread=%s'\ % (vgrid_name,thread_hash) url = "%s?%s" % (os.environ['SCRIPT_URI'], query) notify_subscribers(configuration, forum_base, vgrid_name, '', client_id, url) thread = thread_hash except ValueError, error: post_error = str(error) elif action == 'reply': try: (thread_hash, msg) = reply(forum_base, client_id, msg_body, thread) query = 'vgrid_name=%s&action=show_thread&thread=%s'\ % (vgrid_name,thread_hash) url = "%s?%s" % (os.environ['SCRIPT_URI'], query) notify_subscribers(configuration, forum_base, vgrid_name, '', client_id, url) notify_subscribers(configuration, forum_base, vgrid_name, thread_hash, client_id, url) except ValueError, error: post_error = str(error)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] title_entry = find_entry(output_objects, 'title') label = "%s" % configuration.site_vgrid_label title_entry['text'] = '%s Forum' % label user_settings = title_entry.get('user_settings', {}) (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) vgrid_name = accepted['vgrid_name'][-1] action = accepted['action'][-1] thread = accepted['thread'][-1] msg_subject = accepted['msg_subject'][-1].strip() msg_body = accepted['msg_body'][-1].strip() if not vgrid_is_owner_or_member(vgrid_name, client_id, configuration): output_objects.append({ 'object_type': 'error_text', 'text': '''You must be an owner or member of %s %s to access the forum.''' % (vgrid_name, label) }) return (output_objects, returnvalues.CLIENT_ERROR) if not action in valid_actions: output_objects.append({ 'object_type': 'error_text', 'text': 'Invalid action "%s" (supported: %s)' % (action, ', '.join(valid_actions)) }) return (output_objects, returnvalues.CLIENT_ERROR) if action in post_actions: if not safe_handler(configuration, 'post', op_name, client_id, get_csrf_limit(configuration), accepted): output_objects.append({ 'object_type': 'error_text', 'text': '''Only accepting CSRF-filtered POST requests to prevent unintended updates''' }) return (output_objects, returnvalues.CLIENT_ERROR) # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath( os.path.join(configuration.vgrid_private_base, vgrid_name)) + os.sep forum_base = os.path.abspath(os.path.join(base_dir, '.vgridforum')) # TODO: can we update style inline to avoid explicit themed_styles? title_entry['style'] = themed_styles(configuration, advanced=['forum.css'], user_settings=user_settings) add_import = ''' <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> ''' add_init = ''' function toggle_new(form_elem_id, link_elem_id) { form_elem = document.getElementById(form_elem_id); form_focus = document.getElementById(form_elem_id + "_main"); link_elem = document.getElementById(link_elem_id); if (!form_elem || !link_elem) return; if (form_elem.style.display != 'block') { form_elem.style.display = 'block'; form_focus.focus(); link_elem.style.display = 'none'; } else { form_elem.style.display = 'none'; link_elem.style.display = 'block'; link_elem.focus(); } } ''' add_ready = ''' // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 640, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); // table initially sorted by 0 (last update / date) var sortOrder = [[0,1]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#forumtable").tablesorter({widgets: ["zebra"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); $("#pagerrefresh").click(function() { location.reload(); }); ''' % default_pager_entries title_entry['script']['advanced'] += add_import title_entry['script']['init'] += add_init title_entry['script']['ready'] += add_ready output_objects.append({ 'object_type': 'html_form', 'text': ''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="72" rows="10" id="confirm_input" style="display:none;"></textarea> </div> ''' }) output_objects.append({ 'object_type': 'sectionheader', 'text': '%s Forum for %s' % (label, vgrid_name) }) try: os.makedirs(forum_base) except: pass if not os.path.isdir(forum_base): output_objects.append({ 'object_type': 'error_text', 'text': '''No forum available for %s!''' % vgrid_name }) return (output_objects, returnvalues.SYSTEM_ERROR) post_error = None msg = '' logger.info("vgridforum %s %s %s" % (vgrid_name, action, thread)) if action in post_actions: if action == 'new_thread': try: (thread_hash, msg) = new_subject(forum_base, client_id, msg_subject, msg_body) query = 'vgrid_name=%s&action=show_thread&thread=%s'\ % (vgrid_name, thread_hash) url = "%s?%s" % (os.environ['SCRIPT_URI'], query) notify_subscribers(configuration, forum_base, vgrid_name, '', client_id, url) thread = thread_hash except ValueError, error: post_error = str(error) elif action == 'reply': try: (thread_hash, msg) = reply(forum_base, client_id, msg_body, thread) query = 'vgrid_name=%s&action=show_thread&thread=%s'\ % (vgrid_name, thread_hash) url = "%s?%s" % (os.environ['SCRIPT_URI'], query) notify_subscribers(configuration, forum_base, vgrid_name, '', client_id, url) notify_subscribers(configuration, forum_base, vgrid_name, thread_hash, client_id, url) except ValueError, error: post_error = str(error)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Runtime Environments' (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) # jquery support for tablesorter and confirmation on "leave": title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 500, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); // table initially sorted by col. 2 (admin), then 0 (name) var sortOrder = [[2,1],[0,0]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#runtimeenvtable").tablesorter({widgets: ["zebra", "saveSort"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> ''' % default_pager_entries output_objects.append({'object_type': 'html_form', 'text':''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea> </div> ''' }) output_objects.append({'object_type': 'header', 'text' : 'Runtime Environments'}) output_objects.append( {'object_type': 'text', 'text' : 'Runtime environments specify software/data available on resources.' }) output_objects.append( {'object_type': 'link', 'destination': 'docs.py?show=Runtime+Environments', 'class': 'infolink', 'title': 'Show information about runtime environment', 'text': 'Documentation on runtime environments'}) output_objects.append({'object_type': 'sectionheader', 'text' : 'Existing runtime environments'}) (status, ret) = list_runtime_environments(configuration) if not status: output_objects.append({'object_type': 'error_text', 'text' : ret}) return (output_objects, returnvalues.SYSTEM_ERROR) runtimeenvironments = [] for single_re in ret: (re_dict, msg) = get_re_dict(single_re, configuration) if not re_dict: output_objects.append({'object_type': 'error_text', 'text' : msg}) return (output_objects, returnvalues.SYSTEM_ERROR) re_item = build_reitem_object(configuration, re_dict) re_name = re_item['name'] re_item['viewruntimeenvlink'] = {'object_type': 'link', 'destination': "showre.py?re_name=%s" % re_name, 'class': 'infolink', 'title': 'View %s runtime environment' % re_name, 'text': ''} if client_id == re_item['creator']: js_name = 'delete%s' % hexlify(re_name) helper = html_post_helper(js_name, 'deletere.py', {'re_name': re_name}) output_objects.append({'object_type': 'html_form', 'text': helper}) re_item['ownerlink'] = {'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s');"\ % (js_name, 'Really delete %s?' % re_name), 'class': 'removelink', 'title': 'Delete %s runtime environment' % re_name, 'text': ''} runtimeenvironments.append(re_item) output_objects.append({'object_type': 'table_pager', 'entry_name': 'runtime envs', 'default_entries': default_pager_entries}) output_objects.append({'object_type': 'runtimeenvironments', 'runtimeenvironments': runtimeenvironments}) if configuration.site_swrepo_url: output_objects.append({'object_type': 'sectionheader', 'text': 'Software Packages'}) output_objects.append({'object_type': 'link', 'destination': configuration.site_swrepo_url, 'class': 'swrepolink', 'title': 'Browse available software packages', 'text': 'Open software catalogue for %s' % \ configuration.short_title, }) output_objects.append({'object_type': 'sectionheader', 'text': 'Additional Runtime Environments'}) output_objects.append({'object_type': 'link', 'destination': 'adminre.py', 'class': 'addlink', 'title': 'Specify a new runtime environment', 'text': 'Create a new runtime environment'}) return (output_objects, returnvalues.OK)
def create_monitor(vgrid_name): """Write monitor HTML file for vgrid_name""" html_file = os.path.join(configuration.vgrid_home, vgrid_name, "%s.html" % configuration.vgrid_monitor) print "collecting statistics for VGrid %s" % vgrid_name sleep_secs = configuration.sleep_secs slackperiod = configuration.slackperiod now = time.asctime(time.localtime()) html_vars = { "sleep_secs": sleep_secs, "vgrid_name": vgrid_name, "logo_url": "/images/logo.jpg", "now": now, "short_title": configuration.short_title, } html = get_cgi_html_header( configuration, "%(short_title)s Monitor, VGrid %(vgrid_name)s" % html_vars, "", True, """<meta http-equiv="refresh" content="%(sleep_secs)s" /> """ % html_vars, themed_styles(configuration), """ <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" > $(document).ready(function() { // table initially sorted by col. 1 (name) var sortOrder = [[1,0]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("table.monitor").tablesorter({widgets: ["zebra"], textExtraction: imgTitle, }); $("table.monitor").each(function () { try { $(this).trigger("sorton", [sortOrder]); } catch(err) { /* tablesorter chokes on empty tables - just continue */ } }); } ); </script> """, "", False, ) html += ( """ <!-- end of raw header: this line is used by showvgridmonitor --> <h1>Statistics/monitor for the %(vgrid_name)s VGrid</h1> <div class="generatornote smallcontent"> This page was generated %(now)s (automatic refresh every %(sleep_secs)s secs). </div> """ % html_vars ) # loop and get totals parse_count = 0 queued_count = 0 frozen_count = 0 executing_count = 0 finished_count = 0 failed_count = 0 retry_count = 0 canceled_count = 0 cpucount_requested = 0 cpucount_done = 0 nodecount_requested = 0 nodecount_done = 0 cputime_requested = 0 cputime_done = 0 used_walltime = 0 disk_requested = 0 disk_done = 0 memory_requested = 0 memory_done = 0 runtimeenv_dict = {"": 0} runtimeenv_requested = 0 runtimeenv_done = 0 number_of_jobs = 0 up_count = 0 down_count = 0 slack_count = 0 job_assigned = 0 job_assigned_cpus = 0 gstat = GridStat(configuration, logger) runtimeenv_dict = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "RUNTIMEENVIRONMENT", {}) parse_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "PARSE") queued_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "QUEUED") frozen_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "FROZEN") executing_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "EXECUTING") failed_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "FAILED") retry_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "RETRY") canceled_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "CANCELED") expired_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "EXPIRED") finished_count = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "FINISHED") nodecount_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "NODECOUNT_REQ") nodecount_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "NODECOUNT_DONE") cputime_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "CPUTIME_REQ") cputime_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "CPUTIME_DONE") used_walltime = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "USED_WALLTIME") if used_walltime == 0: used_walltime = datetime.timedelta(0) used_walltime = format_timedelta(used_walltime) disk_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "DISK_REQ") disk_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "DISK_DONE") memory_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "MEMORY_REQ") memory_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "MEMORY_DONE") cpucount_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "CPUCOUNT_REQ") cpucount_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "CPUCOUNT_DONE") runtimeenv_requested = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "RUNTIMEENVIRONMENT_REQ") runtimeenv_done = gstat.get_value(gstat.VGRID, vgrid_name.upper(), "RUNTIMEENVIRONMENT_DONE") number_of_jobs = parse_count number_of_jobs += queued_count number_of_jobs += frozen_count number_of_jobs += expired_count number_of_jobs += canceled_count number_of_jobs += failed_count number_of_jobs += executing_count number_of_jobs += finished_count number_of_jobs += retry_count html_vars = { "parse_count": parse_count, "queued_count": queued_count, "frozen_count": frozen_count, "executing_count": executing_count, "failed_count": failed_count, "retry_count": retry_count, "canceled_count": canceled_count, "expired_count": expired_count, "finished_count": finished_count, "number_of_jobs": number_of_jobs, "cpucount_requested": cpucount_requested, "cpucount_done": cpucount_done, "nodecount_requested": nodecount_requested, "nodecount_done": nodecount_done, "cputime_requested": cputime_requested, "cputime_done": cputime_done, "used_walltime": used_walltime, "disk_requested": disk_requested, "disk_done": disk_done, "memory_requested": memory_requested, "memory_done": memory_done, "runtimeenv_requested": runtimeenv_requested, "runtimeenv_done": runtimeenv_done, } html += ( """<h2>Job Stats</h2><table class=monitorstats><tr><td> <table class=monitorjobs><tr class=title><td>Job State</td><td>Number of jobs</td></tr> <tr><td>Parse</td><td>%(parse_count)s</td></tr> <tr><td>Queued</td><td>%(queued_count)s</td></tr> <tr><td>Frozen</td><td>%(frozen_count)s</td></tr> <tr><td>Executing</td><td>%(executing_count)s</td></tr> <tr><td>Failed</td><td>%(failed_count)s</td></tr> <tr><td>Retry</td><td>%(retry_count)s</td></tr> <tr><td>Canceled</td><td>%(canceled_count)s</td></tr> <tr><td>Expired</td><td>%(expired_count)s</td></tr> <tr><td>Finished</td><td>%(finished_count)s</td></tr> <tr><td>Total</td><td>%(number_of_jobs)s</td></tr> </table> </td><td> <table class=monitorresreq> <tr class=title><td>Requirement</td><td>Requested</td><td>Done</td></tr> <tr><td>Cpucount</td><td>%(cpucount_requested)s</td><td>%(cpucount_done)s</td></tr> <tr><td>Nodecount</td><td>%(nodecount_requested)s</td><td>%(nodecount_done)s</td></tr> <tr><td>Cputime</td><td>%(cputime_requested)s</td><td>%(cputime_done)s</td></tr> <tr><td>GB Disk</td><td>%(disk_requested)s</td><td>%(disk_done)s</td></tr> <tr><td>MB Memory</td><td>%(memory_requested)s</td><td>%(memory_done)s</td></tr> <tr><td>Runtime Envs</td><td>%(runtimeenv_requested)s</td><td>%(runtimeenv_done)s</td></tr> <tr><td>Used Walltime</td><td colspan='2'>%(used_walltime)s</td></tr> </table><br /> </td><td> <div class=monitorruntimeenvdetails> <table class=monitorruntimeenvdone> <tr class=title><td>Runtime Envs Done</td><td></td></tr> """ % html_vars ) if len(runtimeenv_dict.keys()) < 1: # No runtimeenv requests html += "<tr><td></td><td>-</td></tr>\n" else: for entry in runtimeenv_dict.keys(): if not entry == "": html += "<tr><td>" + entry + "</td><td>" + str(runtimeenv_dict[entry]) + "</td></tr>\n" total_number_of_exe_resources, total_number_of_store_resources = 0, 0 total_number_of_exe_cpus, total_number_of_store_gigs = 0, 0 vgrid_name_list = vgrid_name.split("/") current_dir = "" exes, stores = "", "" for vgrid_name_part in vgrid_name_list: current_dir = os.path.join(current_dir, vgrid_name_part) abs_mon_dir = os.path.join(configuration.vgrid_home, current_dir) # print 'dir: %s' % abs_mon_dir # Potential race - just ignore if it disappeared try: sorted_names = os.listdir(abs_mon_dir) except OSError: continue sorted_names.sort() for filename in sorted_names: # print filename if filename.startswith("monitor_last_request_"): # read last request helper file mon_file_name = os.path.join(abs_mon_dir, filename) print "found " + mon_file_name last_request_dict = unpickle(mon_file_name, logger) if not last_request_dict: print "could not open and unpickle: " + mon_file_name continue difference = datetime.datetime.now() - last_request_dict["CREATED_TIME"] days = str(difference.days) hours = str(difference.seconds / 3600) minutes = str((difference.seconds % 3600) / 60) seconds = str((difference.seconds % 60) % 60) if last_request_dict.has_key("CPUTIME"): cputime = last_request_dict["CPUTIME"] elif last_request_dict.has_key("cputime"): cputime = last_request_dict["cputime"] else: print "ERROR: last request does not contain cputime field!: %s" % last_request_dict continue try: cpusec = int(cputime) except ValueError: try: cpusec = int(float(cputime)) except ValueError, verr: print "ERROR: failed to parse cputime %s: %s" % (cputime, verr) # Include execution delay guesstimate for strict fill # LRMS resources try: delay = int(last_request_dict["EXECUTION_DELAY"]) except KeyError: delay = 0 except ValueError: delay = 0 time_remaining = ( last_request_dict["CREATED_TIME"] + datetime.timedelta(seconds=cpusec) + datetime.timedelta(seconds=delay) ) - datetime.datetime.now() days_rem = str(time_remaining.days) hours_rem = str(time_remaining.seconds / 3600) minutes_rem = str((time_remaining.seconds % 3600) / 60) seconds_rem = str((time_remaining.seconds % 60) % 60) if time_remaining.days < -7: try: print "removing: %s as we havent seen him for %s days." % ( mon_file_name, abs(time_remaining).days, ) os.remove(mon_file_name) except Exception, err: print "could not remove: '%s' Error: %s" % (mon_file_name, str(err)) pass else: unique_res_name_and_exe_list = filename.split("monitor_last_request_", 1) if cpusec == 0: resource_status = "unavailable" elif time_remaining.days < 0: # time_remaining.days < 0 means that we have passed the specified time time_rem_abs = abs(time_remaining) if time_rem_abs.days == 0 and int(time_rem_abs.seconds) < int(slackperiod): resource_status = "slack" slack_count = slack_count + 1 else: resource_status = "offline" down_count = down_count + 1 else: resource_status = "online" up_count = up_count + 1 exes += "<tr>" exes += "<td><img src=/images/status-icons/%s.png /></td>" % resource_status public_id = unique_res_name_and_exe_list[1] if last_request_dict["RESOURCE_CONFIG"].get("ANONYMOUS", True): public_id = anon_resource_id(public_id) public_name = last_request_dict["RESOURCE_CONFIG"].get("PUBLICNAME", "") resource_parts = public_id.split("_", 2) resource_name = "<a href='viewres.py?unique_resource_name=%s'>%s</a>" % ( resource_parts[0], resource_parts[0], ) if public_name: resource_name += "<br />(alias %s)" % public_name else: resource_name += "<br />(no alias)" resource_name += "<br />%s" % resource_parts[1] exes += "<td>%s</td>" % resource_name exes += "<td>%s<br />(%sd %sh %sm %ss ago)</td>" % ( time.asctime(last_request_dict["CREATED_TIME"].timetuple()), days, hours, minutes, seconds, ) exes += "<td>" + vgrid_name + "</td>" runtime_envs = last_request_dict["RESOURCE_CONFIG"]["RUNTIMEENVIRONMENT"] re_list_text = ", ".join([i[0] for i in runtime_envs]) exes += '<td title="%s">' % re_list_text + str(len(runtime_envs)) + "</td>" exes += ( "<td>" + str(last_request_dict["RESOURCE_CONFIG"]["CPUTIME"]) + "</td><td>" + str(last_request_dict["RESOURCE_CONFIG"]["NODECOUNT"]) + "</td><td>" + str(last_request_dict["RESOURCE_CONFIG"]["CPUCOUNT"]) + "</td><td>" + str(last_request_dict["RESOURCE_CONFIG"]["DISK"]) + "</td><td>" + str(last_request_dict["RESOURCE_CONFIG"]["MEMORY"]) + "</td><td>" + str(last_request_dict["RESOURCE_CONFIG"]["ARCHITECTURE"]) + "</td>" ) exes += ( "<td>" + last_request_dict["STATUS"] + "</td><td>" + str(last_request_dict["CPUTIME"]) + "</td>" ) exes += "<td class=status_%s>" % resource_status if "unavailable" == resource_status: exes += "-" elif "slack" == resource_status: exes += "Within slack period (%s < %s secs)" % (time_rem_abs.seconds, slackperiod) elif "offline" == resource_status: exes += "down?" else: exes += "%sd, %sh, %sm, %ss" % (days_rem, hours_rem, minutes_rem, seconds_rem) exes += "</td>" exes += "</tr>\n" if last_request_dict["STATUS"] == "Job assigned": job_assigned = job_assigned + 1 job_assigned_cpus = job_assigned_cpus + int( last_request_dict["RESOURCE_CONFIG"]["NODECOUNT"] ) * int(last_request_dict["RESOURCE_CONFIG"]["CPUCOUNT"]) total_number_of_exe_resources += 1 total_number_of_exe_cpus += int(last_request_dict["RESOURCE_CONFIG"]["NODECOUNT"]) * int( last_request_dict["RESOURCE_CONFIG"]["CPUCOUNT"] ) elif filename.startswith("monitor_last_status_"): # store must be linked to this vgrid, not only parent vgrid: # inheritance only covers access, not automatic participation if current_dir != vgrid_name: continue # read last resource action status file mon_file_name = os.path.join(abs_mon_dir, filename) print "found " + mon_file_name last_status_dict = unpickle(mon_file_name, logger) if not last_status_dict: print "could not open and unpickle: " + mon_file_name continue difference = datetime.datetime.now() - last_status_dict["CREATED_TIME"] days = str(difference.days) hours = str(difference.seconds / 3600) minutes = str((difference.seconds % 3600) / 60) seconds = str((difference.seconds % 60) % 60) if last_status_dict["STATUS"] == "stopped": time_stopped = datetime.datetime.now() - last_status_dict["CREATED_TIME"] if time_stopped.days > 7: try: print "removing: %s as we havent seen him for %s days." % ( mon_file_name, abs(time_stopped).days, ) os.remove(mon_file_name) except Exception, err: print "could not remove: '%s' Error: %s" % (mon_file_name, str(err)) continue
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Dashboard' # jquery support for tablesorter and confirmation on "leave": title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" > $(document).ready(function() { function roundNumber(num, dec) { var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec); return result; } $("#jobs_stats").addClass("spinner iconspace"); $("#jobs_stats").html("Loading job stats..."); $("#res_stats").addClass("spinner iconspace"); $("#res_stats").html("Loading resource stats..."); $("#disk_stats").addClass("spinner iconspace"); $("#disk_stats").html("Loading disk stats..."); $("#cert_stats").addClass("spinner iconspace"); $("#cert_stats").html("Loading certificate information..."); /* Run certificate request in the background and handle as soon as results come in */ $.ajax({ url: "userstats.py?output_format=json;stats=certificate", type: "GET", dataType: "json", cache: false, success: function(jsonRes, textStatus) { var i = 0; var certificate = null; var renew_days = 30; var day_msecs = 24*60*60*1000; // Grab results from json response and place them in resource status. for(i=0; i<jsonRes.length; i++) { if (jsonRes[i].object_type == "user_stats") { certificate = jsonRes[i].certificate; $("#cert_stats").removeClass("spinner iconspace"); $("#cert_stats").empty(); if (certificate.expire == -1) { break; } var expire_date = new Date(certificate.expire); $("#cert_stats").append("Your user certificate expires on " + expire_date + "."); // Use date from time diff in ms to avoid calendar mangling var show_renew = new Date(expire_date.getTime() - renew_days*day_msecs); if(new Date().getTime() > show_renew.getTime()) { $("#cert_stats").addClass("warningtext"); $("#cert_stats").append(" <a class=\'certrenewlink\' href=\'reqcert.py\'>Renew certificate</a>."); } break; } } } }); /* Run jobs request in the background and handle as soon as results come in */ $.ajax({ url: "userstats.py?output_format=json;stats=jobs", type: "GET", dataType: "json", cache: false, success: function(jsonRes, textStatus) { var i = 0; var jobs = null; // Grab results from json response and place them in job status. for(i=0; i<jsonRes.length; i++) { if (jsonRes[i].object_type == "user_stats") { jobs = jsonRes[i].jobs; //alert("inspect stats result: " + jobs); $("#jobs_stats").removeClass("spinner iconspace"); $("#jobs_stats").empty(); $("#jobs_stats").append("You have submitted a total of " + jobs.total + " jobs: " + jobs.parse + " parse, " + jobs.queued + " queued, " + jobs.frozen + " frozen, " + jobs.executing + " executing, " + jobs.finished + " finished, " + jobs.retry + " retry, " + jobs.canceled + " canceled, " + jobs.expired + " expired and " + jobs.failed + " failed."); break; } } } }); /* Run resources request in the background and handle as soon as results come in */ $.ajax({ url: "userstats.py?output_format=json;stats=resources", type: "GET", dataType: "json", cache: false, success: function(jsonRes, textStatus) { var i = 0; var resources = null; // Grab results from json response and place them in resource status. for(i=0; i<jsonRes.length; i++) { if (jsonRes[i].object_type == "user_stats") { resources = jsonRes[i].resources; //alert("inspect resources stats result: " + resources); $("#res_stats").removeClass("spinner iconspace"); $("#res_stats").empty(); $("#res_stats").append(resources.resources + " resources providing " + resources.exes + " execution units in total allow execution of your jobs."); break; } } } }); /* Run disk request in the background and handle as soon as results come in */ $.ajax({ url:"userstats.py?output_format=json;stats=disk", type: "GET", dataType: "json", cache: false, success: function(jsonRes, textStatus) { var i = 0; var disk = null; // Grab results from json response and place them in resource status. for(i=0; i<jsonRes.length; i++) { if (jsonRes[i].object_type == "user_stats") { disk = jsonRes[i].disk; //alert("inspect disk stats result: " + disk); $("#disk_stats").removeClass("spinner iconspace"); $("#disk_stats").empty(); $("#disk_stats").append("Your own " + disk.own_files +" files and " + disk.own_directories + " directories take up " + roundNumber(disk.own_megabytes, 2) + " MB in total and you additionally share " + disk.vgrid_files + " files and " + disk.vgrid_directories + " directories of " + roundNumber(disk.vgrid_megabytes, 2) + " MB in total."); break; } } } }); } ); </script> ''' output_objects.append({'object_type': 'header', 'text': 'Dashboard'}) output_objects.append({'object_type': 'sectionheader', 'text' : "Welcome to the %s" % \ configuration.site_title}) welcome_line = "Hi %s" % extract_field(client_id, "full_name") output_objects.append({'object_type': 'text', 'text': welcome_line}) dashboard_info = """ This is your private entry page or your dashboard where you can get a quick status overview and find pointers to help and documentation. When you are logged in with your user credentials/certificate, as you are now, you can navigate your pages using the menu on the left. """ % os.environ output_objects.append({'object_type': 'text', 'text': dashboard_info}) output_objects.append({'object_type': 'sectionheader', 'text': "Your Status"}) output_objects.append({'object_type': 'html_form', 'text': ''' <p> This is a general status overview for your Grid activities. Please note that some of the numbers are cached for a while to keep server load down. </p> <div id="jobs_stats"><!-- for jquery --></div><br /> <div id="res_stats"><!-- for jquery --></div><br /> <div id="disk_stats"><!-- for jquery --></div><br /> <div id="cert_stats"><!-- for jquery --></div><br /> <div id="cert_renew" class="hidden"><a href="reqcert.py">renew certificate</a> </div> '''}) output_objects.append({'object_type': 'sectionheader', 'text' : 'Documentation and Help'}) online_help = """ %s includes some built-in documentation like the """ % configuration.site_title output_objects.append({'object_type': 'text', 'text': online_help}) output_objects.append({'object_type': 'link', 'destination': 'docs.py', 'class': 'infolink', 'title': 'built-in documentation', 'text': 'Docs page'}) project_info = """ but additional help, background information and tutorials are available in the """ output_objects.append({'object_type': 'text', 'text': project_info}) output_objects.append({'object_type': 'link', 'destination': configuration.site_external_doc, 'class': 'urllink', 'title': 'external documentation', 'text': 'external %s documentation' % \ configuration.site_title}) output_objects.append({'object_type': 'sectionheader', 'text' : "Personal Settings"}) settings_info = """ You can customize your personal pages by opening the Settings page from the navigation menu and entering personal preferences. In that way you can completely redecorate your interface and configure things like notification, profile visibility and remote file access. """ output_objects.append({'object_type': 'text', 'text': settings_info}) #env_info = """Env %s""" % os.environ #output_objects.append({'object_type': 'text', 'text': env_info}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) client_dir = client_id_dir(client_id) status = returnvalues.OK defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) show_description = accepted['description'][-1].lower() == 'true' if not configuration.site_enable_jobs: output_objects.append({ 'object_type': 'error_text', 'text': '''Job execution is not enabled on this system''' }) return (output_objects, returnvalues.SYSTEM_ERROR) # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath( os.path.join(configuration.user_home, client_dir)) + os.sep template_path = os.path.join(base_dir, default_mrsl_filename) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Submit Job' user_settings = title_entry.get('user_settings', {}) output_objects.append({'object_type': 'header', 'text': 'Submit Job'}) default_mrsl = get_default_mrsl(template_path) if not user_settings or not user_settings.has_key('SUBMITUI'): logger.info('Settings dict does not have SUBMITUI key - using default') submit_style = configuration.submitui[0] else: submit_style = user_settings['SUBMITUI'] # We generate all 3 variants of job submission (fields, textarea, files), # initially hide them and allow to switch between them using js. # could instead extract valid prefixes as in settings.py # (means: by "eval" from configuration). We stick to hard-coding. submit_options = ['fields_form', 'textarea_form', 'files_form'] open_button_id = 'open_fancy_upload' form_method = 'post' csrf_limit = get_csrf_limit(configuration) fill_helpers = { 'dest_dir': '.' + os.sep, 'fancy_open': open_button_id, 'default_mrsl': default_mrsl, 'form_method': form_method, 'csrf_field': csrf_field, 'csrf_limit': csrf_limit } target_op = 'uploadchunked' csrf_token = make_csrf_token(configuration, form_method, target_op, client_id, csrf_limit) (add_import, add_init, add_ready) = fancy_upload_js(configuration, csrf_token=csrf_token) add_init += ''' submit_options = %s; function setDisplay(this_id, new_d) { //console.log("setDisplay with: "+this_id); el = document.getElementById(this_id) if (el == undefined || el.style == undefined) { console.log("failed to locate display element: "+this_id); return; // avoid js null ref errors } el.style.display=new_d; } function switchTo(name) { //console.log("switchTo: "+name); for (o=0; o < submit_options.length; o++) { if (name == submit_options[o]) { setDisplay(submit_options[o],"block"); } else { setDisplay(submit_options[o],"none"); } } } ''' % submit_options add_ready += ''' switchTo("%s"); setUploadDest("%s"); /* wrap openFancyUpload in function to avoid event data as argument */ $("#%s").click(function() { openFancyUpload(); }); ''' % (submit_style + "_form", fill_helpers['dest_dir'], open_button_id) fancy_dialog = fancy_upload_html(configuration) # TODO: can we update style inline to avoid explicit themed_styles? title_entry['style'] = themed_styles( configuration, base=['jquery.fileupload.css', 'jquery.fileupload-ui.css'], skin=['fileupload-ui.custom.css'], user_settings=user_settings) title_entry['script']['advanced'] += add_import title_entry['script']['init'] += add_init title_entry['script']['ready'] += add_ready output_objects.append({ 'object_type': 'text', 'text': 'This page is used to submit jobs to the grid.' }) output_objects.append({ 'object_type': 'verbatim', 'text': ''' There are %s interface styles available that you can choose among:''' % len(submit_options) }) links = [] for opt in submit_options: name = opt.split('_', 2)[0] links.append({ 'object_type': 'link', 'destination': "javascript:switchTo('%s')" % opt, 'class': 'submit%slink iconspace' % name, 'title': 'Switch to %s submit interface' % name, 'text': '%s style' % name, }) output_objects.append({'object_type': 'multilinkline', 'links': links}) output_objects.append({ 'object_type': 'text', 'text': ''' Please note that changes to the job description are *not* automatically transferred if you switch style.''' }) output_objects.append({ 'object_type': 'html_form', 'text': '<div id="fields_form" style="display:none;">\n' }) # Fields output_objects.append({ 'object_type': 'sectionheader', 'text': 'Please fill in your job description in the fields' ' below:' }) output_objects.append({ 'object_type': 'text', 'text': """ Please fill in one or more fields below to define your job before hitting Submit Job at the bottom of the page. Empty fields will simply result in the default value being used and each field is accompanied by a help link providing further details about the field.""" }) fill_helpers.update({'fancy_dialog': fancy_dialog}) target_op = 'submitfields' csrf_token = make_csrf_token(configuration, form_method, target_op, client_id, csrf_limit) fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token}) output_objects.append({ 'object_type': 'html_form', 'text': """ <div class='submitjob'> <form method='%(form_method)s' action='%(target_op)s.py'> <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' /> """ % fill_helpers }) show_fields = get_job_specs(configuration) try: parsed_mrsl = dict(parse_lines(default_mrsl)) except: parsed_mrsl = {} # Find allowed VGrids and Runtimeenvironments and add them to # configuration object for automated choice handling vgrid_access = user_vgrid_access(configuration, client_id) + \ [any_vgrid] vgrid_access.sort() configuration.vgrids = vgrid_access (re_status, allowed_run_envs) = list_runtime_environments(configuration) if not re_status: logger.error('Failed to extract allowed runtime envs: %s' % allowed_run_envs) allowed_run_envs = [] allowed_run_envs.sort() configuration.runtimeenvironments = allowed_run_envs # TODO: next call is slow because we walk and reload all pickles user_res = user_allowed_res_exes(configuration, client_id) # Add valid MAXFILL values to automated choice handling configuration.maxfills = [keyword_all] + maxfill_fields # Allow any exe unit on all allowed resources allowed_resources = ['%s_*' % res for res in user_res.keys()] allowed_resources.sort() configuration.resources = allowed_resources field_size = 30 area_cols = 80 area_rows = 5 for (field, spec) in show_fields: title = spec['Title'] if show_description: description = '%s<br />' % spec['Description'] else: description = '' field_type = spec['Type'] # Use saved value and fall back to default if it is missing saved = parsed_mrsl.get('::%s::' % field, None) if saved: if not spec['Type'].startswith('multiple'): default = saved[0] else: default = saved else: default = spec['Value'] # Hide sandbox field if sandboxes are disabled if field == 'SANDBOX' and not configuration.site_enable_sandboxes: continue if 'invisible' == spec['Editor']: continue if 'custom' == spec['Editor']: continue output_objects.append({ 'object_type': 'html_form', 'text': """ <b>%s:</b> <a class='infolink iconspace' href='docs.py?show=job#%s'> help</a><br /> %s""" % (title, field, description) }) if 'input' == spec['Editor']: if field_type.startswith('multiple'): output_objects.append({ 'object_type': 'html_form', 'text': """ <textarea class='fillwidth padspace' name='%s' cols='%d' rows='%d'>%s</textarea><br /> """ % (field, area_cols, area_rows, '\n'.join(default)) }) elif field_type == 'int': output_objects.append({ 'object_type': 'html_form', 'text': """ <input type='number' name='%s' size='%d' value='%s' min=0 required pattern='[0-9]+' /><br /> """ % (field, field_size, default) }) else: output_objects.append({ 'object_type': 'html_form', 'text': """ <input type='text' name='%s' size='%d' value='%s' /><br /> """ % (field, field_size, default) }) elif 'select' == spec['Editor']: choices = available_choices(configuration, client_id, field, spec) res_value = default value_select = '' if field_type.startswith('multiple'): value_select += '<div class="scrollselect">' for name in choices: # Blank default value does not make sense here if not str(name): continue selected = '' if str(name) in res_value: selected = 'checked' value_select += ''' <input type="checkbox" name="%s" %s value=%s>%s<br /> ''' % (field, selected, name, name) value_select += '</div>\n' else: value_select += "<select name='%s'>\n" % field for name in choices: selected = '' if str(res_value) == str(name): selected = 'selected' display = "%s" % name if display == '': display = ' ' value_select += """<option %s value='%s'>%s</option>\n""" \ % (selected, name, display) value_select += """</select><br />\n""" output_objects.append({ 'object_type': 'html_form', 'text': value_select }) output_objects.append({'object_type': 'html_form', 'text': "<br />"}) output_objects.append({ 'object_type': 'html_form', 'text': """ <br /> <table class='centertext'> <tr><td><input type='submit' value='Submit Job' /> <input type='checkbox' name='save_as_default'> Save as default job template </td></tr> </table> <br /> </form> </div> """ }) output_objects.append({ 'object_type': 'html_form', 'text': ''' </div><!-- fields_form--> <div id="textarea_form" style="display:none;"> ''' }) # Textarea output_objects.append({ 'object_type': 'sectionheader', 'text': 'Please enter your mRSL job description below:' }) output_objects.append({ 'object_type': 'html_form', 'text': """ <div class='smallcontent'> Job descriptions can use a wide range of keywords to specify job requirements and actions.<br /> Each keyword accepts one or more values of a particular type.<br /> The full list of keywords with their default values and format is available in the on-demand <a href='docs.py?show=job'>mRSL Documentation</a>. <p> Actual examples for inspiration: <a href=/public/cpuinfo.mRSL>CPU Info</a>, <a href=/public/basic-io.mRSL>Basic I/O</a>, <a href=/public/notification.mRSL>Job Notification</a>, <a href=/public/povray.mRSL>Povray</a> and <a href=/public/vcr.mRSL>VCR</a> </div> """ }) target_op = 'textarea' csrf_token = make_csrf_token(configuration, form_method, target_op, client_id, csrf_limit) fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token}) output_objects.append({ 'object_type': 'html_form', 'text': """ <!-- Please note that textarea.py chokes if no nonempty KEYWORD_X_Y_Z fields are supplied: thus we simply send a bogus jobname which does nothing --> <form method='%(form_method)s' action='%(target_op)s.py'> <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' /> <table class='submitjob'> <tr><td class='centertext'> <input type=hidden name=jobname_0_0_0 value=' ' /> <textarea class='fillwidth padspace' rows='25' name='mrsltextarea_0'> %(default_mrsl)s </textarea> </td></tr> <tr><td class='centertext'> <input type='submit' value='Submit Job' /> <input type='checkbox' name='save_as_default' >Save as default job template </td></tr> </table> </form> """ % fill_helpers }) output_objects.append({ 'object_type': 'html_form', 'text': ''' </div><!-- textarea_form--> <div id="files_form" style="display:none;"> ''' }) # Upload form output_objects.append({ 'object_type': 'sectionheader', 'text': 'Please upload your job file or packaged job files' ' below:' }) output_objects.append({ 'object_type': 'html_form', 'text': """ <form enctype='multipart/form-data' method='%(form_method)s' action='%(target_op)s.py'> <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' /> <table class='files'> <tr class='title'><td class='centertext' colspan=4> Upload job files </td></tr> <tr><td colspan=3> Upload file to current directory (%(dest_dir)s) </td><td><br /></td></tr> <tr><td colspan=2> Extract package files (.zip, .tar.gz, .tar.bz2) </td><td colspan=2> <input type=checkbox name='extract_0' /> </td></tr> <tr><td colspan=2> Submit mRSL files (also .mRSL files included in packages) </td><td colspan=2> <input type=checkbox name='submitmrsl_0' checked /> </td></tr> <tr><td> File to upload </td><td class='righttext' colspan=3> <input name='fileupload_0_0_0' type='file'/> </td></tr> <tr><td> Optional remote filename (extra useful in windows) </td><td class='righttext' colspan=3> <input name='default_remotefilename_0' type='hidden' value='%(dest_dir)s'/> <input name='remotefilename_0' type='text' size='50' value='%(dest_dir)s'/> <input type='submit' value='Upload' name='sendfile'/> </td></tr> </table> </form> <table class='files'> <tr class='title'><td class='centertext'> Upload other files efficiently (using chunking). </td></tr> <tr><td class='centertext'> <button id='%(fancy_open)s'>Open Upload dialog</button> </td></tr> </table> </div><!-- files_form--> %(fancy_dialog)s """ % fill_helpers }) return (output_objects, status)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'People' # jquery support for tablesorter and confirmation on "leave": title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 640, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); // table initially sorted by 0 (name) var sortOrder = [[0,0]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#usertable").tablesorter({widgets: ["zebra", "saveSort"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> ''' % default_pager_entries output_objects.append({'object_type': 'html_form', 'text':''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="72" rows="10" id="confirm_input" style="display:none;"></textarea> </div> ''' }) output_objects.append({'object_type': 'header', 'text' : 'People'}) output_objects.append( {'object_type': 'text', 'text' : 'View and communicate with other users.' }) output_objects.append({'object_type': 'sectionheader', 'text' : 'All users'}) visible_user = user_visible_user_confs(configuration, client_id) allow_vgrids = user_allowed_vgrids(configuration, client_id) anon_map = anon_to_real_user_map(configuration.user_home) if not visible_user: output_objects.append({'object_type': 'error_text', 'text' : 'no users found!'}) return (output_objects, returnvalues.SYSTEM_ERROR) users = [] for (visible_user_id, user_dict) in visible_user.items(): user_id = visible_user_id if visible_user_id in anon_map.keys(): user_id = anon_map[visible_user_id] user_obj = {'object_type': 'user', 'name': visible_user_id} user_obj.update(user_dict) user_obj['userdetailslink'] = \ {'object_type': 'link', 'destination': 'viewuser.py?cert_id=%s'\ % quote(visible_user_id), 'class': 'infolink', 'title': 'View details for %s' % \ visible_user_id, 'text': ''} vgrids_allow_email = user_dict[CONF].get('VGRIDS_ALLOW_EMAIL', []) vgrids_allow_im = user_dict[CONF].get('VGRIDS_ALLOW_IM', []) if any_vgrid in vgrids_allow_email: email_vgrids = allow_vgrids else: email_vgrids = set(vgrids_allow_email).intersection(allow_vgrids) if any_vgrid in vgrids_allow_im: im_vgrids = allow_vgrids else: im_vgrids = set(vgrids_allow_im).intersection(allow_vgrids) for proto in configuration.notify_protocols: if not email_vgrids and proto == 'email': continue if not im_vgrids and proto != 'email': continue if user_obj[CONF].get(proto.upper(), None): js_name = 'send%s%s' % (proto, hexlify(visible_user_id)) helper = html_post_helper(js_name, 'sendrequestaction.py', {'cert_id': visible_user_id, 'request_type': 'plain', 'protocol': proto, 'request_text': ''}) output_objects.append({'object_type': 'html_form', 'text': helper}) link = 'send%slink' % proto user_obj[link] = {'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s', '%s');"\ % (js_name, 'Really send %s message to %s?'\ % (proto, visible_user_id), 'request_text'), 'class': link, 'title': 'Send %s message to %s' % \ (proto, visible_user_id), 'text': ''} users.append(user_obj) output_objects.append({'object_type': 'table_pager', 'entry_name': 'people', 'default_entries': default_pager_entries}) output_objects.append({'object_type': 'user_list', 'users': users}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) client_dir = client_id_dir(client_id) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath(os.path.join(configuration.user_home, client_dir)) + os.sep title_entry = find_entry(output_objects, 'title') title_entry['text'] = 'Settings' # prepare support for toggling the views (by css/jquery) title_entry['style'] = themed_styles(configuration) title_entry['style']['skin'] += ''' %s ''' % cm_css title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> %s <script type="text/javascript" > var toggleHidden = function(classname) { // classname supposed to have a leading dot $(classname).toggleClass("hidden"); } $(document).ready(function() { } ); </script> ''' % cm_javascript valid_topics = ['general', 'style'] active_menu = extract_menu(configuration, title_entry) if 'submitjob' in active_menu: valid_topics.append('job') if 'people' in active_menu: valid_topics.append('profile') if configuration.site_script_deps: valid_topics.append('widgets') if configuration.arc_clusters: valid_topics.append('arc') if configuration.site_enable_sftp: valid_topics.append('sftp') if configuration.site_enable_davs: valid_topics.append('webdavs') if configuration.site_enable_ftps: valid_topics.append('ftps') topics = accepted['topic'] # Backwards compatibility if topics and topics[0] == 'ssh': topics[0] = 'sftp' topics = [i for i in topics if i in valid_topics] # Default to general if no valid topics given if not topics: topics.append(valid_topics[0]) topic_titles = dict([(i, i.title()) for i in valid_topics]) for (key, val) in [('sftp', 'SFTP'), ('webdavs', 'WebDAVS'), ('ftps', 'FTPS')]: if key in valid_topics: topic_titles[key] = val output_objects.append({'object_type': 'header', 'text' : 'Settings'}) links = [] for name in valid_topics: active_menu = '' if topics[0] == name: active_menu = 'activebutton' links.append({'object_type': 'link', 'destination': "settings.py?topic=%s" % name, 'class': '%ssettingslink settingsbutton %s' % \ (name, active_menu), 'title': 'Switch to %s settings' % topic_titles[name], 'text' : '%s' % topic_titles[name], }) output_objects.append({'object_type': 'multilinkline', 'links': links, 'sep': ' '}) output_objects.append({'object_type': 'text', 'text': ''}) # load current settings current_settings_dict = load_settings(client_id, configuration) if not current_settings_dict: # no current settings found current_settings_dict = {} if not topics: output_objects.append({'object_type': 'error_text', 'text': 'No valid topics!'}) return (output_objects, returnvalues.CLIENT_ERROR) if 'general' in topics: html = \ ''' <div id="settings"> <form method="post" action="settingsaction.py"> <table class="settings fixedlayout"> <tr class="title"><td class="centertext"> Select your %s settings </td></tr> <tr><td> </td></tr> <tr><td> <input type="hidden" name="topic" value="general" /> Please note that if you want to set multiple values (e.g. addresses) in the same field, you must write each value on a separate line but without blank lines. </td></tr> <tr><td> </td></tr> ''' % configuration.short_title settings_entries = get_settings_specs() for (keyword, val) in settings_entries: if 'SUBMITUI' == keyword and \ 'job' not in valid_topics: continue if 'notify' == val['Context'] and \ keyword.lower() not in configuration.notify_protocols: continue entry = \ """ <tr class='title'><td> %s </td></tr> <tr><td> %s </td></tr> <tr><td> """\ % (keyword.replace('_', ' ').title(), val['Description']) if val['Type'] == 'multiplestrings': try: # get valid choices from conf. multiple selections valid_choices = eval('configuration.%s' % keyword.lower()) current_choice = [] if current_settings_dict.has_key(keyword): current_choice = current_settings_dict[keyword] if len(valid_choices) > 0: entry += '<div class="scrollselect">' for choice in valid_choices: selected = '' if choice in current_choice: selected = 'checked' entry += ''' <input type="checkbox" name="%s" %s value="%s">%s<br />''' % \ (keyword, selected, choice, choice) entry += '</div>' else: entry = '' except: # failed on evaluating configuration.%s area = ''' <textarea id="%s" cols=40 rows=1 name="%s">''' % \ (keyword, keyword) if current_settings_dict.has_key(keyword): area += '\n'.join(current_settings_dict[keyword]) area += '</textarea>' entry += wrap_edit_area(keyword, area, general_edit, 'BASIC') elif val['Type'] == 'string': # get valid choices from conf valid_choices = eval('configuration.%s' % keyword.lower()) current_choice = '' if current_settings_dict.has_key(keyword): current_choice = current_settings_dict[keyword] if len(valid_choices) > 0: entry += '<select name="%s">' % keyword for choice in valid_choices: selected = '' if choice == current_choice: selected = 'selected' entry += '<option %s value="%s">%s</option>'\ % (selected, choice, choice) entry += '</select><br />' else: entry = '' elif val['Type'] == 'boolean': current_choice = '' if current_settings_dict.has_key(keyword): current_choice = current_settings_dict[keyword] entry += '<select name="%s">' % keyword for choice in (True, False): selected = '' if choice == current_choice: selected = 'selected' entry += '<option %s value="%s">%s</option>'\ % (selected, choice, choice) entry += '</select><br />' html += """%s </td></tr> """ % entry html += \ """ <tr><td> <input type="submit" value="Save General Settings" /> </td></tr> </table> </form> </div> """ output_objects.append({'object_type': 'html_form', 'text': html}) if 'job' in topics: mrsl_path = os.path.join(base_dir, default_mrsl_filename) default_mrsl = get_default_mrsl(mrsl_path) html = \ ''' <div id="defaultmrsl"> <form method="post" action="editfile.py"> <table class="defaultjob fixedlayout"> <tr class="title"><td class="centertext"> Default job on submit page </td></tr> <tr><td> </td></tr> <tr><td> If you use the same fields and values in many of your jobs, you can save your preferred job description here to always start out with that description on your submit job page. </td></tr> <tr><td> </td></tr> <tr><td> <input type="hidden" name="path" value="%(mrsl_template)s" /> <input type="hidden" name="newline" value="unix" /> ''' keyword = "defaultjob" area = ''' <textarea id="%(keyword)s" cols=82 rows=25 name="editarea"> %(default_mrsl)s </textarea> ''' html += wrap_edit_area(keyword, area, cm_options, 'BASIC') html += ''' </td></tr> <tr><td> <input type="submit" value="Save Job Template" /> </td></tr> </table> </form> </div> ''' html = html % { 'default_mrsl': default_mrsl, 'mrsl_template': default_mrsl_filename, 'site': configuration.short_title, 'keyword': keyword } output_objects.append({'object_type': 'html_form', 'text': html}) if 'style' in topics: css_path = os.path.join(base_dir, default_css_filename) default_css = get_default_css(css_path) html = \ ''' <div id="defaultcss"> <form method="post" action="editfile.py"> <table class="defaultstyle fixedlayout"> <tr class="title"><td class="centertext"> Default CSS (style) for all pages </td></tr> <tr><td> </td></tr> <tr><td> If you want to customize the look and feel of the %(site)s web interfaces you can override default values here. If you leave the style file blank you will just use the default style.<br /> You can copy paste from the available style file links below if you want to override specific parts.<br /> <div class="warningtext">Please note that you can not save an empty style file, but must at least leave a blank line to use defaults. Additionally some errors in your style code may potentially cause severe corruption in your page layouts, so it may be a good idea to keep another browser tab/window ready to (re)move your .default.css file to restore the defaults while experimenting here. </div> </td></tr> <tr><td> <a class="urllink" href="/images/default.css">default</a> , <a class="urllink" href="/images/bluesky.css">bluesky</a> </td></tr> <tr><td> </td></tr> <tr><td> <input type="hidden" name="path" value="%(css_template)s" /> <input type="hidden" name="newline" value="unix" /> ''' keyword = "defaultstyle" area = ''' <textarea id="%(keyword)s" cols=82 rows=25 min_len=1 name="editarea"> %(default_css)s </textarea> ''' html += wrap_edit_area(keyword, area, style_edit) html += ''' </td></tr> <tr><td> <input type="submit" value="Save Style Settings" /> </td></tr> </table> </form> </div> ''' html = html % { 'default_css': default_css, 'css_template': default_css_filename, 'site': configuration.short_title, 'keyword': keyword } output_objects.append({'object_type': 'html_form', 'text': html}) if 'widgets' in topics: # load current widgets current_widgets_dict = load_widgets(client_id, configuration) if not current_widgets_dict: # no current widgets found current_widgets_dict = {} show_widgets = current_settings_dict.get('ENABLE_WIDGETS', True) if show_widgets: edit_widgets = '''You can simply copy/paste from the available widget file links below if you want to reuse existing widgets.<br /> </td></tr> <tr><td> <a class="urllink" href="/images/widgets/hello-grid.app">hello grid</a>, <a class="urllink" href="/images/widgets/simple-calendar.app">simple calendar</a>, <a class="urllink" href="/images/widgets/calendar.app">calendar</a>, <a class="urllink" href="/images/widgets/gcal.app">google calendar</a>, <a class="urllink" href="/images/widgets/calculator.app">calculator</a>, <a class="urllink" href="/images/widgets/localrss.app">local rss reader</a>, <a class="urllink" href="/images/widgets/rss.app">rss reader</a>, <a class="urllink" href="/images/widgets/clock.app">clock</a>, <a class="urllink" href="/images/widgets/weather.app">weather</a>, <a class="urllink" href="/images/widgets/progressbar.app">progress bar</a>, <a class="urllink" href="/images/widgets/simple-move.app">simple-move</a>, <a class="urllink" href="/images/widgets/portlets.app">portlets</a>, <a class="urllink" href="/images/widgets/countdown.app">countdown</a>, <a class="urllink" href="/images/widgets/sparkline.app">mini chart</a>, <a class="urllink" href="/images/widgets/piechart.app">pie chart</a>, <a class="urllink" href="/images/widgets/simple-jobmon.app">simple-jobmon</a>, <a class="urllink" href="/images/widgets/cert-countdown.app">certificate countdown</a>, <a class="urllink" href="/images/widgets/disk-use.app">disk use progress bar</a>, <a class="urllink" href="/images/widgets/jobs-stats.app">jobs stats table</a>, <a class="urllink" href="/images/widgets/jobs-stats-chart.app">jobs stats chart</a>, <a class="urllink" href="/images/widgets/daily-wm-comic.app">Daily WulffMorgenthaler comic</a>, <a class="urllink" href="/images/widgets/kunet-login.app">KUnet login</a> <a class="urllink" href="/images/widgets/tdchotspot-login.app">TDC Hotspot login</a> </td></tr> <tr><td> <div class="warningtext">Please note that the widgets parser is rather grumpy so you may have to avoid blank lines in your widget code below. Additionally any errors in your widgets code may cause severe corruption in your pages, so it may be a good idea to keep another browser tab/window ready for emergency disabling of widgets while experimenting here.</div> </td></tr> <tr><td> <input type="hidden" name="topic" value="widgets" /> </td></tr> <tr><td> ''' html = \ '''<div id="widgets"> <form method="post" action="settingsaction.py"> <table class="widgets fixedlayout"> <tr class="title"><td class="centertext"> Default user defined widgets for all pages </td></tr> <tr><td> </td></tr> <tr><td> If you want to customize the look and feel of the %s web interfaces you can add your own widgets here. If you leave the widgets blank you will just get the default empty widget spaces.<br /> ''' % configuration.short_title widgets_entries = get_widgets_specs() widgets_html = '' for (keyword, val) in widgets_entries: widgets_html += \ """ <tr class=title><td> %s </td></tr> <tr><td> %s </td></tr> <tr><td> """\ % (keyword.replace('_', ' ').title(), val['Description']) if val['Type'] == 'multiplestrings': try: # get valid choices from conf. multiple selections valid_choices = eval('configuration.%s' % keyword.lower()) current_choice = [] if current_widgets_dict.has_key(keyword): current_choice = current_widgets_dict[keyword] if len(valid_choices) > 0: widgets_html += '<div class="scrollselect">' for choice in valid_choices: selected = '' if choice in current_choice: selected = 'checked' widgets_html += ''' <input type="checkbox" name="%s" %s value="%s">%s<br />'''\ % (keyword, selected, choice, choice) widgets_html += '</div>' except: area = \ """<textarea id='%s' cols=78 rows=10 name='%s'>""" % \ (keyword, keyword) if current_widgets_dict.has_key(keyword): area += '\n'.join(current_widgets_dict[keyword]) area += '</textarea>' widgets_html += wrap_edit_area(keyword, area, widgets_edit) if show_widgets: edit_widgets += ''' %s <tr><td> <input type="submit" value="Save Widgets Settings" /> </td></tr> ''' % widgets_html else: edit_widgets = ''' <br/> <div class="warningtext"> Widgets are disabled on your <em>General</em> settings page. Please enable them there first if you want to customize your grid pages. </div> ''' html += \ ''' %s </table> </form> </div> ''' % edit_widgets output_objects.append({'object_type': 'html_form', 'text': html}) if 'profile' in topics: # load current profile current_profile_dict = load_profile(client_id, configuration) if not current_profile_dict: # no current profile found current_profile_dict = {} (got_list, all_vgrids) = vgrid_list_vgrids(configuration) if not got_list: all_vgrids = [] all_vgrids.append(any_vgrid) all_vgrids.sort() configuration.vgrids_allow_email = all_vgrids configuration.vgrids_allow_im = all_vgrids images = [] for path in os.listdir(base_dir): real_path = os.path.join(base_dir, path) if os.path.splitext(path)[1].strip('.') in profile_img_extensions \ and os.path.getsize(real_path) < profile_img_max_kb*1024: images.append(path) configuration.public_image = images html = \ ''' <div id="profile"> <form method="post" action="settingsaction.py"> <table class="profile fixedlayout"> <tr class="title"><td class="centertext"> Public profile information visible to other users. </td></tr> <tr><td> </td></tr> <tr><td> If you want to let other users know more about you can add your own text here. If you leave the text area blank you will just get the default empty profile information.<br /> </td></tr> <tr><td> <div class="warningtext">Please note that the profile parser is rather grumpy so you may have to avoid blank lines in your text below. </div> </td></tr> <tr><td> <input type="hidden" name="topic" value="profile" /> </td></tr> <tr><td> ''' profile_entries = get_profile_specs() for (keyword, val) in profile_entries: # Mask VGrid name if configured mask_title = keyword.replace( 'VGRID', configuration.site_vgrid_label.upper()) mask_desc = val['Description'].replace( 'VGrid', configuration.site_vgrid_label) html += \ """ <tr class=title><td> %s </td></tr> <tr><td> %s </td></tr> <tr><td> """ % (mask_title.replace('_', ' ').title(), html_escape(mask_desc)) if val['Type'] == 'multiplestrings': try: # get valid choices from conf. multiple selections valid_choices = eval('configuration.%s' % keyword.lower()) current_choice = [] if current_profile_dict.has_key(keyword): current_choice = current_profile_dict[keyword] if len(valid_choices) > 0: html += '<div class="scrollselect">' for choice in valid_choices: selected = '' if choice in current_choice: selected = 'checked' html += ''' <input type="checkbox" name="%s" %s value="%s">%s<br />''' % \ (keyword, selected, choice, choice) html += '</div>' except: area = \ """<textarea id='%s' cols=78 rows=10 name='%s'>""" % \ (keyword, keyword) if current_profile_dict.has_key(keyword): area += '\n'.join(current_profile_dict[keyword]) area += '</textarea>' html += wrap_edit_area(keyword, area, profile_edit) elif val['Type'] == 'boolean': valid_choices = [True, False] current_choice = '' if current_profile_dict.has_key(keyword): current_choice = current_profile_dict[keyword] if len(valid_choices) > 0: html += '<select name="%s">' % keyword for choice in valid_choices: selected = '' if choice == current_choice: selected = 'selected' html += '<option %s value="%s">%s</option>'\ % (selected, choice, choice) html += '</select><br />' html += ''' <tr><td> <input type="submit" value="Save Profile Settings" /> </td></tr> </table> </form> </div> ''' output_objects.append({'object_type': 'html_form', 'text': html}) if 'sftp' in topics: # load current ssh/sftp current_ssh_dict = load_ssh(client_id, configuration) if not current_ssh_dict: # no current ssh found current_ssh_dict = {} default_authkeys = current_ssh_dict.get('authkeys', '') default_authpassword = current_ssh_dict.get('authpassword', '') username = client_alias(client_id) if configuration.user_sftp_alias: username = extract_field(client_id, configuration.user_sftp_alias) create_alias_link(username, client_id, configuration.user_home) sftp_server = configuration.user_sftp_show_address sftp_port = configuration.user_sftp_show_port html = \ ''' <div id="sshaccess"> <form method="post" action="settingsaction.py"> <table class="sshsettings fixedlayout"> <tr class="title"><td class="centertext"> SFTP access to your %(site)s account </td></tr> <tr><td> </td></tr> <tr><td> You can configure SFTP login to your %(site)s account for efficient file access. On Linux/UN*X it also allows transparent access through SSHFS. <br/> <h3>Login Details</h3> <ul> <li>Host <em>%(sftp_server)s</em></li> <li>Port <em>%(sftp_port)s</em></li> <li>Username <em>%(username)s</em></li> <li>%(auth_methods)s <em>as you choose below</em></li> </ul> </td></tr> <tr><td> <input type="hidden" name="topic" value="sftp" /> <div class="div-sftp-client-notes hidden"> <a href="javascript:toggleHidden('.div-sftp-client-notes');" class="removeitemlink" title="Toggle view"> Show less SFTP client details...</a> <h3>Graphical SFTP access</h3> The FireFTP plugin for Firefox is known to generally work for graphical access to your %(site)s home over SFTP. Enter the following values in the FireFTP Account Manager: <pre> Host %(sftp_server)s Login %(username)s Password YOUR_PASSWORD_HERE (passphrase if you configured public key access) Security SFTP Port %(sftp_port)s Private Key ~/.mig/key.pem (if you configured public key access) </pre> other graphical clients may work as well. <h3>Command line SFTP/SSHFS access on Linux/UN*X</h3> Save something like the following lines in your local ~/.ssh/config to avoid typing the full login details every time:<br /> <pre> Host %(sftp_server)s Hostname %(sftp_server)s User %(username)s Port %(sftp_port)s IdentityFile ~/.mig/key.pem </pre> From then on you can use sftp and sshfs to access your %(site)s home: <pre> sftp %(sftp_server)s </pre> <pre> sshfs %(sftp_server)s: mig-home -o uid=$(id -u) -o gid=$(id -g) </pre> You can also integrate with ordinary mounts by adding a line like: <pre> sshfs#%(username)s@%(sftp_server)s: /home/USER/mig-home fuse noauto,user,port=%(sftp_port)d 0 0 </pre> to your /etc/fstab . </div> <div class="div-sftp-client-notes"> <a href="javascript:toggleHidden('.div-sftp-client-notes');" class="additemlink" title="Toggle view">Show more SFTP client details... </a> </div> ''' keyword_keys = "authkeys" if 'publickey' in configuration.user_sftp_auth: html += ''' </td></tr> <tr><td> <h3>Authorized Public Keys</h3> You can use any existing RSA key, or create a new one. If you signed up with a x509 user certificate, you should also have received such a key.pem along with your user certificate. In any case you need to save the contents of the corresponding public key (X.pub) in the text area below, to be able to connect with username and key as described in the Login Details. <br/> ''' area = ''' <textarea id="%(keyword_keys)s" cols=82 rows=5 name="publickeys"> %(default_authkeys)s </textarea> ''' html += wrap_edit_area(keyword_keys, area, ssh_edit, 'BASIC') html += ''' (leave empty to disable sftp access with public keys) </td></tr> ''' keyword_password = "******" if 'password' in configuration.user_sftp_auth: # We only want a single password and a masked input field html += ''' <tr><td> <h3>Authorized Password</h3> Please enter and save your desired password in the text field below, to be able to connect with username and password as described in the Login Details. <br/> <input type=password id="%(keyword_password)s" size=40 name="password" value="%(default_authpassword)s" /> (leave empty to disable sftp access with password) </td></tr> ''' html += ''' <tr><td> <input type="submit" value="Save SFTP Settings" /> </td></tr> ''' html += ''' </table> </form> </div> ''' html = html % { 'default_authkeys': default_authkeys, 'default_authpassword': default_authpassword, 'site': configuration.short_title, 'keyword_keys': keyword_keys, 'keyword_password': keyword_password, 'username': username, 'sftp_server': sftp_server, 'sftp_port': sftp_port, 'auth_methods': ' / '.join(configuration.user_sftp_auth).title(), } output_objects.append({'object_type': 'html_form', 'text': html}) if 'webdavs' in topics: # load current davs current_davs_dict = load_davs(client_id, configuration) if not current_davs_dict: # no current davs found current_davs_dict = {} default_authkeys = current_davs_dict.get('authkeys', '') default_authpassword = current_davs_dict.get('authpassword', '') username = client_alias(client_id) if configuration.user_davs_alias: username = extract_field(client_id, configuration.user_davs_alias) create_alias_link(username, client_id, configuration.user_home) davs_server = configuration.user_davs_show_address davs_port = configuration.user_davs_show_port html = \ ''' <div id="davsaccess"> <form method="post" action="settingsaction.py"> <table class="davssettings fixedlayout"> <tr class="title"><td class="centertext"> WebDAVS access to your %(site)s account </td></tr> <tr><td> </td></tr> <tr><td> You can configure WebDAVS login to your %(site)s account for transparent file access from your PC or workstation.<br/> <h3>Login Details</h3> <ul> <li>Host <em>%(davs_server)s</em></li> <li>Port <em>%(davs_port)s</em></li> <li>Username <em>%(username)s</em></li> <li>%(auth_methods)s <em>as you choose below</em></li> </ul> </td></tr> <tr><td> <input type="hidden" name="topic" value="webdavs" /> <div class="div-webdavs-client-notes hidden"> <a href="javascript:toggleHidden('.div-webdavs-client-notes');" class="removeitemlink" title="Toggle view"> Show less WebDAVS client details...</a> <h3>Graphical WebDAVS access</h3> Several native file browsers and web browsers are known to generally work for graphical access to your %(site)s home over WebDAVS. <br /> Enter the address https://%(davs_server)s:%(davs_port)s and when fill in the login details: <pre> Username %(username)s Password YOUR_PASSWORD_HERE </pre> other graphical clients should work as well. <h3>Command line WebDAVS access on Linux/UN*X</h3> Save something like the following lines in your local ~/.netrc to avoid typing the full login details every time:<br /> <pre> machine %(davs_server)s login %(username)s password YOUR_PASSWORD_HERE </pre> From then on you can use e.g. cadaver or fusedav to access your %(site)s home: <pre> cadaver https://%(davs_server)s:%(davs_port)s </pre> <pre> fusedav https://%(davs_server)s:%(davs_port)s mig-home -o uid=$(id -u) -o gid=$(id -g) </pre> </div> <div class="div-webdavs-client-notes"> <a href="javascript:toggleHidden('.div-webdavs-client-notes');" class="additemlink" title="Toggle view"> Show more WebDAVS client details...</a> </div> ''' keyword_keys = "authkeys" if 'publickey' in configuration.user_davs_auth: html += ''' </td></tr> <tr><td> <h3>Authorized Public Keys</h3> You can use any existing RSA key, including the key.pem you received along with your user certificate, or create a new one. In any case you need to save the contents of the corresponding public key (X.pub) in the text area below, to be able to connect with username and key as described in the Login Details. <br/>''' area = ''' <textarea id="%(keyword_keys)s" cols=82 rows=5 name="publickeys"> %(default_authkeys)s </textarea> ''' html += wrap_edit_area(keyword_keys, area, davs_edit, 'BASIC') html += ''' (leave empty to disable davs access with public keys) </td></tr> ''' keyword_password = "******" if 'password' in configuration.user_davs_auth: # We only want a single password and a masked input field html += ''' <tr><td> <h3>Authorized Password</h3> Please enter and save your desired password in the text field below, to be able to connect with username and password as described in the Login Details. <br/> <input type=password id="%(keyword_password)s" size=40 name="password" value="%(default_authpassword)s" /> (leave empty to disable davs access with password) </td></tr> ''' html += ''' <tr><td> <input type="submit" value="Save WebDAVS Settings" /> </td></tr> ''' html += ''' </table> </form> </div> ''' html = html % { 'default_authkeys': default_authkeys, 'default_authpassword': default_authpassword, 'site': configuration.short_title, 'keyword_keys': keyword_keys, 'keyword_password': keyword_password, 'username': username, 'davs_server': davs_server, 'davs_port': davs_port, 'auth_methods': ' / '.join(configuration.user_davs_auth).title(), } output_objects.append({'object_type': 'html_form', 'text': html}) if 'ftps' in topics: # load current ftps current_ftps_dict = load_ftps(client_id, configuration) if not current_ftps_dict: # no current ftps found current_ftps_dict = {} default_authkeys = current_ftps_dict.get('authkeys', '') default_authpassword = current_ftps_dict.get('authpassword', '') username = client_alias(client_id) if configuration.user_ftps_alias: username = extract_field(client_id, configuration.user_ftps_alias) create_alias_link(username, client_id, configuration.user_home) ftps_server = configuration.user_ftps_show_address ftps_ctrl_port = configuration.user_ftps_show_ctrl_port html = \ ''' <div id="ftpsaccess"> <form method="post" action="settingsaction.py"> <table class="ftpssettings fixedlayout"> <tr class="title"><td class="centertext"> FTPS access to your %(site)s account </td></tr> <tr><td> </td></tr> <tr><td> You can configure FTPS login to your %(site)s account for efficient file access.<br/> <h3>Login Details</h3> <ul> <li>Host <em>%(ftps_server)s</em></li> <li>Port <em>%(ftps_ctrl_port)s</em></li> <li>Username <em>%(username)s</em></li> <li>%(auth_methods)s <em>as you choose below</em></li> </ul> </td></tr> <tr><td> <input type="hidden" name="topic" value="ftps" /> <div class="div-ftps-client-notes hidden"> <a href="javascript:toggleHidden('.div-ftps-client-notes');" class="removeitemlink" title="Toggle view"> Show less FTPS client details...</a> <h3>Graphical FTPS access</h3> The FireFTP plugin for Firefox is known to generally work for graphical access to your %(site)s home over FTPS. Enter the following values in the FireFTP Account Manager: <pre> Host %(ftps_server)s Login %(username)s Password YOUR_PASSWORD_HERE Security FTPS Port %(ftps_ctrl_port)s </pre> Other FTP clients and web browsers may work as well if you enter the address ftps://%(ftps_server)s:%(ftps_ctrl_port)s and fill in the login details when prompted: <pre> Username %(username)s Password YOUR_PASSWORD_HERE </pre> <h3>Command line FTPS access on Linux/UN*X</h3> Save something like the following lines in your local ~/.netrc to avoid typing the full login details every time:<br /> <pre> machine %(ftps_server)s login %(username)s password YOUR_PASSWORD_HERE </pre> From then on you can use e.g. lftp or CurlFtpFS to access your %(site)s home: <!-- TODO: we need to provide the intermediate cert for server cert check like this set ssl:ca-file sub.class1.server.ca.pem --> <pre> lftp -e "set ssl:verify-certificate no; set ftp:ssl-protect-data on" \\ -p %(ftps_ctrl_port)s %(ftps_server)s </pre> <pre> curlftpfs -o ssl %(ftps_server)s:%(ftps_ctrl_port)s mig-home \\ -o user=%(username)s -ouid=$(id -u) -o gid=$(id -g) -o no_verify_peer </pre> </div> <div class="div-ftps-client-notes"> <a href="javascript:toggleHidden('.div-ftps-client-notes');" class="additemlink" title="Toggle view">Show more FTPS client details... </a> </div> ''' keyword_keys = "authkeys" if 'publickey' in configuration.user_ftps_auth: html += ''' </td></tr> <tr><td> <h3>Authorized Public Keys</h3> You can use any existing RSA key, including the key.pem you received along with your user certificate, or create a new one. In any case you need to save the contents of the corresponding public key (X.pub) in the text area below, to be able to connect with username and key as described in the Login Details. <br/> ''' area = ''' <textarea id="%(keyword_keys)s" cols=82 rows=5 name="publickeys"> %(default_authkeys)s </textarea> ''' html += wrap_edit_area(keyword_keys, area, ftps_edit, 'BASIC') html += ''' (leave empty to disable ftps access with public keys) </td></tr> ''' keyword_password = "******" if 'password' in configuration.user_ftps_auth: # We only want a single password and a masked input field html += ''' <tr><td> <h3>Authorized Password</h3> Please enter and save your desired password in the text field below, to be able to connect with username and password as described in the Login Details. <br/> <input type=password id="%(keyword_password)s" size=40 name="password" value="%(default_authpassword)s" /> (leave empty to disable ftps access with password) </td></tr> ''' html += ''' <tr><td> <input type="submit" value="Save FTPS Settings" /> </td></tr> ''' html += ''' </table> </form> </div> ''' html = html % { 'default_authkeys': default_authkeys, 'default_authpassword': default_authpassword, 'site': configuration.short_title, 'keyword_keys': keyword_keys, 'keyword_password': keyword_password, 'username': username, 'ftps_server': ftps_server, 'ftps_ctrl_port': ftps_ctrl_port, 'auth_methods': ' / '.join(configuration.user_ftps_auth).title(), } output_objects.append({'object_type': 'html_form', 'text': html}) # if ARC-enabled server: if 'arc' in topics: # provide information about the available proxy, offer upload try: home_dir = os.path.normpath(base_dir) session_Ui = arc.Ui(home_dir, require_user_proxy=True) proxy = session_Ui.getProxy() if proxy.IsExpired(): # can rarely happen, constructor will throw exception output_objects.append({'object_type': 'text', 'text': 'Proxy certificate is expired.'}) else: output_objects.append({'object_type': 'text', 'text': 'Proxy for %s' \ % proxy.GetIdentitySN()}) output_objects.append( {'object_type': 'text', 'text': 'Proxy certificate will expire on %s (in %s sec.)' % (proxy.Expires(), proxy.getTimeleft()) }) except arc.NoProxyError, err: output_objects.append({'object_type':'warning', 'text': 'No proxy certificate to load: %s' \ % err.what()}) output_objects = output_objects + arc.askProxy()
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) action = accepted['action'][-1] req_list = accepted['req_id'] job_list = accepted['job_id'] lines = int(accepted['lines'][-1]) meta = '''<meta http-equiv="refresh" content="%s" /> ''' % configuration.sleep_secs style = themed_styles(configuration) script = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"> </script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 500, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); // table initially sorted by col. 9 (created) var sortOrder = [[9,0]]; $("#certreqtable").tablesorter({widgets: ["zebra", "saveSort"], sortList:sortOrder }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> ''' % default_pager_entries title_entry = find_entry(output_objects, 'title') title_entry['text'] = '%s administration panel' % configuration.short_title title_entry['meta'] = meta title_entry['style'] = style title_entry['javascript'] = script output_objects.append({'object_type': 'html_form', 'text':''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea> </div> ''' }) if not is_admin(client_id, configuration, logger): output_objects.append( {'object_type': 'error_text', 'text' : 'You must be an admin to access this control panel.'}) return (output_objects, returnvalues.CLIENT_ERROR) html = '' if action and not action in grid_actions.keys() + certreq_actions: output_objects.append({'object_type': 'error_text', 'text' : 'Invalid action: %s' % action}) return (output_objects, returnvalues.SYSTEM_ERROR) if action in grid_actions: msg = "%s" % grid_actions[action] if job_list: msg += ' %s' % ' '.join(job_list) msg += '\n' if not send_message_to_grid_script(msg, logger, configuration): output_objects.append( {'object_type': 'error_text', 'text' : '''Error sending %s message to grid_script.''' % action }) status = returnvalues.SYSTEM_ERROR elif action in certreq_actions: if action == "addcertreq": for req_id in req_list: if accept_cert_req(req_id, configuration): output_objects.append( {'object_type': 'text', 'text': 'Accepted certificate request %s' % req_id}) else: output_objects.append( {'object_type': 'error_text', 'text': 'Accept certificate request failed - details in log' }) elif action == "delcertreq": for req_id in req_list: if delete_cert_req(req_id, configuration): output_objects.append( {'object_type': 'text', 'text': 'Deleted certificate request %s' % req_id}) else: output_objects.append( {'object_type': 'error_text', 'text': 'Delete certificate request failed - details in log' }) show, drop = '', '' general = """ <h1>Server Status</h1> <p class='importanttext'> This page automatically refreshes every %s seconds. </p> <p> You can see the current grid daemon status and server logs below. The buttons provide access to e.g. managing the grid job queues. </p> <form method='get' action='migadmin.py'> <input type='hidden' name='action' value='' /> <input type='submit' value='Show last log lines' /> <input type='text' size='2' name='lines' value='%s' /> </form> <br /> <form method='get' action='migadmin.py'> <input type='hidden' name='lines' value='%s' /> <input type='hidden' name='action' value='reloadconfig' /> <input type='submit' value='Reload Configuration' /> </form> <br /> """ % (configuration.sleep_secs, lines, lines) show += """ <form method='get' action='migadmin.py'> <input type='hidden' name='lines' value='%s' /> <input type='submit' value='Log Jobs' /> <select name='action'> """ % lines drop += """ <form method='get' action='migadmin.py'> <input type='hidden' name='lines' value='%s' /> <input type='submit' value='Drop Job' /> <select name='action'> """ % lines for queue in ['queued', 'executing', 'done']: selected = '' if action.find(queue) != -1: selected = 'selected' show += "<option %s value='show%s'>%s</option>" % (selected, queue, queue) drop += "<option %s value='drop%s'>%s</option>" % (selected, queue, queue) show += """ </select> </form> <br /> """ drop += """ </select> <input type='text' size='20' name='job_id' value='' /> </form> <br /> """ html += general html += show html += drop daemons = """ <div id='daemonstatus'> """ daemon_names = ['grid_script.py', 'grid_monitor.py', 'grid_sshmux.py'] # No need to run im_notify unless any im notify protocols are enabled if [i for i in configuration.notify_protocols if i != 'email']: daemon_names.append('grid_imnotify.py') if configuration.site_enable_sftp: daemon_names.append('grid_sftp.py') if configuration.site_enable_davs: daemon_names.append('grid_webdavs.py') if configuration.site_enable_ftps: daemon_names.append('grid_ftps.py') if configuration.site_enable_openid: daemon_names.append('grid_openid.py') for proc in daemon_names: pgrep_proc = subprocess.Popen(['pgrep', '-f', proc], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) pgrep_proc.wait() ps_out = pgrep_proc.stdout.read().strip() if pgrep_proc.returncode == 0: daemons += "<div class='status_online'>%s running (pid %s)</div>" \ % (proc, ps_out) else: daemons += "<div class='status_offline'>%s not running!</div>" % \ proc daemons += """</div> <br /> """ html += daemons output_objects.append({'object_type': 'header', 'text' : 'Pending Certificate Requests'}) (status, ret) = list_cert_reqs(configuration) if not status: logger.error("%s: failed for '%s': %s" % (op_name, client_id, ret)) output_objects.append({'object_type': 'error_text', 'text' : ret}) return (output_objects, returnvalues.SYSTEM_ERROR) certreqs = [] for req_id in ret: (load_status, req_dict) = get_cert_req(req_id, configuration) if not load_status: logger.error("%s: load failed for '%s': %s" % \ (op_name, req_id, req_dict)) output_objects.append({'object_type': 'error_text', 'text' : 'Could not read details for "%s"' % \ req_id}) return (output_objects, returnvalues.SYSTEM_ERROR) req_item = build_certreqitem_object(configuration, req_dict) js_name = 'create%s' % req_id helper = html_post_helper(js_name, 'migadmin.py', {'action': 'addcertreq', 'req_id': req_id}) output_objects.append({'object_type': 'html_form', 'text': helper}) req_item['addcertreqlink'] = { 'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s');" % \ (js_name, 'Really accept %s?' % req_id), 'class': 'addlink', 'title': 'Accept %s' % req_id, 'text': ''} js_name = 'delete%s' % req_id helper = html_post_helper(js_name, 'migadmin.py', {'action': 'delcertreq', 'req_id': req_id}) output_objects.append({'object_type': 'html_form', 'text': helper}) req_item['delcertreqlink'] = { 'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s');" % \ (js_name, 'Really remove %s?' % req_id), 'class': 'removelink', 'title': 'Remove %s' % req_id, 'text': ''} certreqs.append(req_item) output_objects.append({'object_type': 'table_pager', 'entry_name': 'pending certificate requests', 'default_entries': default_pager_entries}) output_objects.append({'object_type': 'certreqs', 'certreqs': certreqs}) log_path_list = [] if os.path.isabs(configuration.logfile): log_path_list.append(configuration.logfile) else: log_path_list.append(os.path.join(configuration.log_dir, configuration.logfile)) for log_path in log_path_list: html += ''' <h1>%s</h1> <textarea rows=%s cols=200 readonly="readonly"> ''' % (log_path, lines) try: logger.debug("loading %d lines from %s" % (lines, log_path)) log_fd = open(log_path, 'r') log_fd.seek(0, os.SEEK_END) size = log_fd.tell() pos = log_fd.tell() log_lines = [] step_size = 100 # locate last X lines while pos > 0 and len(log_lines) < lines: offset = min(lines * step_size, size) logger.debug("seek to offset %d from end of %s" % (offset, log_path)) log_fd.seek(-offset, os.SEEK_END) pos = log_fd.tell() log_lines = log_fd.readlines() step_size *= 2 logger.debug("reading %d lines from %s" % (lines, log_path)) html += ''.join(log_lines[-lines:]) log_fd.close() except Exception, exc: logger.error("reading %d lines from %s: %s" % (lines, log_path, exc)) output_objects.append({'object_type': 'error_text', 'text' : 'Error reading log (%s)' % exc}) return (output_objects, returnvalues.SYSTEM_ERROR) html += '''</textarea>
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False, op_menu=False) client_dir = client_id_dir(client_id) defaults = signature()[1] (validate_status, accepted) = validate_input(user_arguments_dict, defaults, output_objects, allow_rejects=False) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) title_entry = find_entry(output_objects, 'title') title_entry['text'] = '%s certificate request' % configuration.short_title title_entry['skipmenu'] = True form_fields = ['full_name', 'organization', 'email', 'country', 'state', 'password', 'verifypassword', 'comment'] title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = cert_js_helpers(form_fields) output_objects.append({'object_type': 'html_form', 'text':''' <div id="contextual_help"> <div class="help_gfx_bubble"><!-- graphically connect field with help text --></div> <div class="help_message"><!-- filled by js --></div> </div> ''' }) header_entry = {'object_type': 'header', 'text' : 'Welcome to the %s certificate request page' % \ configuration.short_title} output_objects.append(header_entry) # Please note that base_dir must end in slash to avoid access to other # user dirs when own name is a prefix of another user name base_dir = os.path.abspath(os.path.join(configuration.user_home, client_dir)) + os.sep user_fields = {'full_name': '', 'organization': '', 'email': '', 'state': '', 'country': '', 'password': '', 'verifypassword': ''} if not os.path.isdir(base_dir) and client_id: # Redirect to extcert page with certificate requirement but without # changing access method (CGI vs. WSGI). extcert_url = os.environ['REQUEST_URI'].replace('-sid', '-bin') extcert_url = os.path.join(os.path.dirname(extcert_url), 'extcert.py') extcert_link = {'object_type': 'link', 'destination': extcert_url, 'text': 'Sign up with existing certificate (%s)' % client_id} output_objects.append({'object_type': 'warning', 'text' : 'Apparently you already have a suitable %s certificate that you may sign up with:' % \ configuration.short_title }) output_objects.append(extcert_link) output_objects.append({'object_type': 'warning', 'text' : 'However, if you want a dedicated %s certificate you can still request one below:' % \ configuration.short_title }) elif client_id: for entry in (title_entry, header_entry): entry['text'] = entry['text'].replace('request', 'request / renew') output_objects.append({'object_type': 'html_form', 'text' : '''<p> Apparently you already have a valid %s certificate, but if it is about to expire you can renew it by posting the form below. Renewal with changed fields is <span class=mandatory>not</span> supported, so all fields including your original password must remain unchanged for renew to work. Otherwise it results in a request for a new account and certificate without access to your old files, jobs and privileges.</p>''' % \ configuration.short_title}) user_fields.update(distinguished_name_to_user(client_id)) user_fields.update({ 'valid_name_chars': html_escape(valid_name_chars), 'valid_password_chars': html_escape(valid_password_chars), 'password_min_len': password_min_len, 'password_max_len': password_max_len, 'site': configuration.short_title }) output_objects.append({'object_type': 'html_form', 'text' : """ Please enter your information in at least the <span class=mandatory>mandatory</span> fields below and press the Send button to submit the certificate request to the %(site)s administrators. <p class='criticaltext highlight_message'> IMPORTANT: Please help us verify your identity by providing Organization and Email data that we can easily validate!<br /> That is, if You're a student/employee at KU, please enter institute acronym (NBI, DIKU, etc.) in the Organization field and use your corresponding [email protected] or USER@*.ku.dk address in the Email field. </p> <hr /> <div class=form_container> <!-- use post here to avoid field contents in URL --> <form method=post action=reqcertaction.py onSubmit='return validate_form();'> <table> <tr><td class='mandatory label'>Full name</td><td><input id='full_name_field' type=text name=cert_name value='%(full_name)s' /></td><td class=fill_space><br /></td></tr> <tr><td class='mandatory label'>Email address</td><td><input id='email_field' type=text name=email value='%(email)s' /> </td><td class=fill_space><br /></td></tr> <tr><td class='mandatory label'>Organization</td><td><input id='organization_field' type=text name=org value='%(organization)s' /></td><td class=fill_space><br /></td></tr> <tr><td class='mandatory label'>Two letter country-code</td><td><input id='country_field' type=text name=country maxlength=2 value='%(country)s' /></td><td class=fill_space><br /></td></tr> <tr><td class='optional label'>State</td><td><input id='state_field' type=text name=state value='%(state)s' /> </td><td class=fill_space><br /></td></tr> <tr><td class='mandatory label'>Password</td><td><input id='password_field' type=password name=password maxlength=%(password_max_len)s value='%(password)s' /> </td><td class=fill_space><br /></td></tr> <tr><td class='mandatory label'>Verify password</td><td><input id='verifypassword_field' type=password name=verifypassword maxlength=%(password_max_len)s value='%(verifypassword)s' /></td><td class=fill_space><br /></td></tr> <tr><td class='optional label'>Optional comment or reason why you should<br />be granted a %(site)s certificate:</td><td><textarea id='comment_field' rows=4 name=comment></textarea></td><td class=fill_space><br /></td></tr> <tr><td class='label'><!-- empty area --></td><td><input id='submit_button' type=submit value=Send /></td><td class=fill_space><br /></td></tr> </table> </form> </div> <hr /> <br /> <div class='warn_message'>Please note that passwords may be accessible to the %(site)s administrators!</div> <br /> <!-- Hidden help text --> <div id='help_text'> <div id='full_name_help'>Your full name, restricted to the characters in '%(valid_name_chars)s'</div> <div id='organization_help'>Organization name or acronym matching email</div> <div id='email_help'>Email address associated with your organization if at all possible</div> <div id='country_help'>Country code of your organization and on the form DE/DK/GB/US/.. , <a href='http://www.iso.org/iso/country_codes/iso_3166_code_lists/country_names_and_code_elements.html'>help</a></div> <div id='state_help'>Optional state of your organization, please just leave empty unless it is in the US or similar</div> <div id='password_help'>Password is restricted to the characters in '%(valid_password_chars)s and must be %(password_min_len)s to %(password_max_len)s characters long'</div> <div id='verifypassword_help'>Please repeat password</div> <div id='comment_help'>Optional, but a short informative comment may help us verify your certificate needs and thus speed up our response.</div> </div> """ % user_fields}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) meta = ( """<meta http-equiv="refresh" content="%s" /> """ % configuration.sleep_secs ) style = themed_styles(configuration) script = """ <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" > $(document).ready(function() { // table initially sorted by col. 1 (name) var sortOrder = [[1,0]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("table.monitor").tablesorter({widgets: ["zebra"], textExtraction: imgTitle, }); $("table.monitor").each(function () { try { $(this).trigger("sorton", [sortOrder]); } catch(err) { /* tablesorter chokes on empty tables - just continue */ } }); } ); </script> """ title_entry = find_entry(output_objects, "title") title_entry["text"] = "%s Monitor" % configuration.short_title title_entry["meta"] = meta title_entry["style"] = style title_entry["javascript"] = script allowed_vgrids = user_allowed_vgrids(configuration, client_id) vgrid_list = accepted["vgrid_name"] if all_vgrids in accepted["vgrid_name"]: vgrid_list = [i for i in vgrid_list if all_vgrids != i] + allowed_vgrids # Force list to sequence of unique entries for vgrid_name in set(vgrid_list): html = "" if not vgrid_is_owner_or_member(vgrid_name, client_id, configuration): output_objects.append( { "object_type": "error_text", "text": """You must be an owner or member of %s %s to access the monitor.""" % (vgrid_name, configuration.site_vgrid_label), } ) return (output_objects, returnvalues.CLIENT_ERROR) monitor_file = os.path.join(configuration.vgrid_home, vgrid_name, "%s.html" % configuration.vgrid_monitor) try: monitor_fd = open(monitor_file, "r") past_header = False for line in monitor_fd: if -1 != line.find("end of raw header"): past_header = True continue if not past_header: continue if -1 != line.find("begin raw footer:"): break html += str(line) monitor_fd.close() except Exception, exc: output_objects.append( { "object_type": "error_text", "text": "Error reading %s monitor page (%s)" % (configuration.site_vgrid_label, exc), } ) return (output_objects, returnvalues.SYSTEM_ERROR) output_objects.append({"object_type": "html_form", "text": html})
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) vgrid_name = accepted['vgrid_name'][-1] # prepare support for confirm dialog and toggling the views (by css/jquery) title_entry = find_entry(output_objects, 'title') title_entry['text'] = "Administrate %s: %s" % \ (configuration.site_vgrid_label, vgrid_name) title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" src="/images/js/jquery.confirm.js"></script> <script type="text/javascript" > var toggleHidden = function(classname) { // classname supposed to have a leading dot $(classname).toggleClass('hidden'); } $(document).ready(function() { // init confirmation dialog $( "#confirm_dialog" ).dialog( // see http://jqueryui.com/docs/dialog/ for options { autoOpen: false, modal: true, closeOnEscape: true, width: 500, buttons: { "Cancel": function() { $( "#" + name ).dialog("close"); } } }); } ); </script> ''' output_objects.append({'object_type': 'html_form', 'text':''' <div id="confirm_dialog" title="Confirm" style="background:#fff;"> <div id="confirm_text"><!-- filled by js --></div> <textarea cols="40" rows="4" id="confirm_input" style="display:none;"></textarea> </div> ''' }) output_objects.append({'object_type': 'header', 'text' : "Administrate '%s'" % vgrid_name }) if not vgrid_is_owner(vgrid_name, client_id, configuration): output_objects.append({'object_type': 'error_text', 'text': 'Only owners of %s can administrate it.' % vgrid_name }) js_name = 'reqvgridowner%s' % hexlify(vgrid_name) helper = html_post_helper(js_name, 'sendrequestaction.py', {'vgrid_name': vgrid_name, 'request_type': 'vgridowner', 'request_text': ''}) output_objects.append({'object_type': 'html_form', 'text': helper}) output_objects.append( {'object_type': 'link', 'destination': "javascript: confirmDialog(%s, '%s', '%s');"\ % (js_name, "Request ownership of " + \ vgrid_name + ":<br/>" + \ "\nPlease write a message to the owners below.", 'request_text'), 'class': 'addadminlink', 'title': 'Request ownership of %s' % vgrid_name, 'text': 'Apply to become an owner'}) return (output_objects, returnvalues.SYSTEM_ERROR) for (item, scr) in zip(['owner', 'member', 'resource'], ['vgridowner', 'vgridmember', 'vgridres']): output_objects.append({'object_type': 'sectionheader', 'text': "%ss" % item.title() }) if item == 'trigger': # Always run as rule creator to avoid users being able to act on # behalf of ANY other user using triggers (=exploit) extra_fields = [('path', None), ('changes', [keyword_all] + valid_trigger_changes), ('run_as', client_id), ('action', [keyword_auto] + valid_trigger_actions), ('arguments', None)] else: extra_fields = [] (status, oobjs) = vgrid_add_remove_table(client_id, vgrid_name, item, scr, configuration, extra_fields) if not status: output_objects.extend(oobjs) return (output_objects, returnvalues.SYSTEM_ERROR) else: output_objects.append({'object_type': 'html_form', 'text': '<div class="div-%s">' % item }) output_objects.append( {'object_type': 'link', 'destination': "javascript:toggleHidden('.div-%s');" % item, 'class': 'removeitemlink', 'title': 'Toggle view', 'text': 'Hide %ss' % item.title() }) output_objects.extend(oobjs) output_objects.append( {'object_type': 'html_form', 'text': '</div><div class="hidden div-%s">' % item}) output_objects.append( {'object_type': 'link', 'destination': "javascript:toggleHidden('.div-%s');" % item, 'class': 'additemlink', 'title': 'Toggle view', 'text': 'Show %ss' % item.title() }) output_objects.append({'object_type': 'html_form', 'text': '</div>' }) # Checking/fixing of missing components output_objects.append({'object_type': 'sectionheader', 'text': "Repair/Add Components"}) output_objects.append({'object_type': 'html_form', 'text': ''' <form method="post" action="updatevgrid.py"> <input type="hidden" name="vgrid_name" value="%(vgrid)s" /> <input type="submit" value="Repair components" /> </form> ''' % {'vgrid': vgrid_name}}) output_objects.append({'object_type': 'sectionheader', 'text': "Delete %s " % vgrid_name}) output_objects.append({'object_type': 'html_form', 'text': ''' To delete <b>%(vgrid)s</b> remove all members and owners ending with yourself. ''' % {'vgrid': vgrid_name}}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, require_user=False ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) title_entry = find_entry(output_objects, 'title') title_entry['text'] = '%s certificate sign up' % configuration.short_title title_entry['skipmenu'] = True form_fields = ['cert_id', 'cert_name', 'organization', 'email', 'country', 'state', 'comment'] title_entry['style'] = themed_styles(configuration) title_entry['javascript'] = cert_js_helpers(form_fields) output_objects.append({'object_type': 'html_form', 'text':''' <div id="contextual_help"> <div class="help_gfx_bubble"><!-- graphically connect field with help text--></div> <div class="help_message"><!-- filled by js --></div> </div> ''' }) header_entry = {'object_type': 'header', 'text' : 'Welcome to the %s certificate sign up page' % \ configuration.short_title} output_objects.append(header_entry) # Redirect to reqcert page without certificate requirement but without # changing access method (CGI vs. WSGI). certreq_url = os.environ['REQUEST_URI'].replace('-bin', '-sid') certreq_url = os.path.join(os.path.dirname(certreq_url), 'reqcert.py') certreq_link = {'object_type': 'link', 'destination': certreq_url, 'text': 'Request a new %s certificate' % \ configuration.short_title } new_user = distinguished_name_to_user(client_id) # If cert auto create is on, add user without admin interaction if configuration.auto_add_cert_user == False: extcertaction = 'extcertaction.py' else: extcertaction = 'autocreate.py' output_objects.append({'object_type': 'html_form', 'text' : """ This page is used to sign up for %(site)s with an existing certificate from a Certificate Authority (CA) allowed for %(site)s. You can use it if you already have a x509 certificate from another accepted CA. In this way you can simply use your existing certificate for %(site)s access instead of requesting a new one. <br /> The page tries to auto load any certificate your browser provides and fill in the fields accordingly, but in case it can't guess all <span class=mandatory>mandatory</span> fields, you still need to fill in those.<br /> Please enter any missing information below and press the Send button to submit the external certificate sign up request to the %(site)s administrators. <p class='criticaltext highlight_message'>IMPORTANT: Please help us verify your identity by providing Organization and Email data that we can easily validate!<br /> That is, if You're a student/employee at KU, please enter institute acronym (NBI, DIKU, etc.) in the Organization field and use your corresponding [email protected] or USER@*.ku.dk address in the Email field.</p> <hr /> <div class=form_container> <!-- use post here to avoid field contents in URL --> <form method=post action=%(extcertaction)s onSubmit='return validate_form();'> <table> <tr><td class='mandatory label'>Certificate DN</td><td><input id='cert_id_field' type=text size=%(dn_max_len)s maxlength=%(dn_max_len)s name=cert_id value='%(client_id)s' /></td><td class=fill_space></td></tr> <tr><td class='mandatory label'>Full name</td><td><input id='cert_name_field' type=text name=cert_name value='%(common_name)s' /></td><td class=fill_space></td></tr> <tr><td class='mandatory label'>Email address</td><td><input id='email_field' type=text name=email value='%(email)s' /></td><td class=fill_space></td></tr> <tr><td class='mandatory label'>Organization</td><td><input id='organization_field' type=text name=org value='%(org)s' /></td><td class=fill_space></td></tr> <tr><td class='mandatory label'>Two letter country-code</td><td><input id='country_field' type=text name=country maxlength=2 value='%(country)s' /></td><td class=fill_space></td></tr> <tr><td class='optional label'>State</td><td><input id='state_field' type=text name=state value='%(state)s' /></td><td class=fill_space></td></tr> <tr><td class='optional label'>Comment or reason why you should<br />be granted a %(site)s certificate:</td><td><textarea id='comment_field' rows=4 name=comment></textarea></td><td class=fill_space></td></tr> <tr><td class='label'><!-- empty area --></td><td><input id='submit_button' type='submit' value='Send' /></td><td class=fill_space></td></tr> </table> </form> </div> <!-- Hidden help text --> <div id='help_text'> <div id='cert_id_help'>Must be the exact Distinguished Name (DN) of your certificate</div> <div id='cert_name_help'>Your full name, restricted to the characters in '%(valid_name_chars)s'</div> <div id='organization_help'>Organization name or acronym matching email</div> <div id='email_help'>Email address associated with your organization if at all possible</div> <div id='country_help'>Country code of your organization and on the form DE/DK/GB/US/.. , <a href='http://www.iso.org/iso/country_codes/iso_3166_code_lists/country_names_and_code_elements.html'>help</a></div> <div id='state_help'>Optional state of your organization, please just leave empty unless it is in the US or similar</div> <div id='comment_help'>Optional, but a short informative comment may help us verify your certificate needs and thus speed up our response.</div> </div> """ % { 'extcertaction': extcertaction, 'valid_name_chars': valid_name_chars, 'client_id': client_id, 'dn_max_len': dn_max_len, 'common_name': new_user.get('full_name', ''), 'org': new_user.get('organization', ''), 'email': new_user.get('email', ''), 'state': new_user.get('state', ''), 'country': new_user.get('country', ''), 'site': configuration.short_title, }}) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) flavor = accepted['flavor'][-1].strip() freeze_id = accepted['freeze_id'][-1].strip() if not flavor in freeze_flavors.keys(): output_objects.append({ 'object_type': 'error_text', 'text': 'Invalid freeze flavor: %s' % flavor }) return (output_objects, returnvalues.CLIENT_ERROR) title = freeze_flavors[flavor]['adminfreeze_title'] output_objects.append({'object_type': 'header', 'text': title}) title_entry = find_entry(output_objects, 'title') title_entry['text'] = title if not configuration.site_enable_freeze: output_objects.append({ 'object_type': 'text', 'text': '''Freezing archives is disabled on this site. Please contact the site admins %s if you think it should be enabled. ''' % configuration.admin_email }) return (output_objects, returnvalues.OK) # Load existing freeze for stepwise construction if requested freeze_dict = {'ID': freeze_id, 'FLAVOR': flavor} if freeze_id != keyword_auto: (load_status, freeze_dict) = get_frozen_archive(client_id, freeze_id, configuration, checksum_list=[]) if not load_status: logger.error("%s: load failed for '%s': %s" % (op_name, freeze_id, brief_freeze(freeze_dict))) output_objects.append({ 'object_type': 'error_text', 'text': 'Could not read details for "%s"' % freeze_id }) return (output_objects, returnvalues.SYSTEM_ERROR) logger.debug("%s: loaded freeze: %s" % (op_name, brief_freeze(freeze_dict))) # Preserve already saved flavor flavor = freeze_dict.get('FLAVOR', 'freeze') freeze_state = freeze_dict.get('STATE', keyword_final) if freeze_state == keyword_final: logger.error("%s tried to edit finalized %s archive %s" % (client_id, flavor, freeze_id)) output_objects.append({ 'object_type': 'error_text', 'text': 'You cannot edit finalized %s archive %s' % (flavor, freeze_id) }) output_objects.append({ 'object_type': 'link', 'destination': 'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id, flavor), 'class': 'viewarchivelink iconspace genericbutton', 'title': 'View details about your %s archive' % flavor, 'text': 'View details', }) return (output_objects, returnvalues.CLIENT_ERROR) elif freeze_state == keyword_updating: logger.error("%s tried to edit %s archive %s under update" % (client_id, flavor, freeze_id)) output_objects.append({ 'object_type': 'error_text', 'text': 'You cannot edit %s archive %s until active update completes' % (flavor, freeze_id) }) output_objects.append({ 'object_type': 'link', 'destination': 'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id, flavor), 'class': 'viewarchivelink iconspace genericbutton', 'title': 'View details about your %s archive' % flavor, 'text': 'View details', }) return (output_objects, returnvalues.CLIENT_ERROR) form_method = 'post' csrf_limit = get_csrf_limit(configuration) fill_helpers = { 'flavor': flavor, 'form_method': form_method, 'csrf_field': csrf_field, 'csrf_limit': csrf_limit } target_op = 'uploadchunked' csrf_token = make_csrf_token(configuration, form_method, target_op, client_id, csrf_limit) fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token}) lookup_map = { 'freeze_id': 'ID', 'freeze_name': 'NAME', 'freeze_author': 'AUTHOR', 'freeze_description': 'DESCRIPTION' } for (key, val) in lookup_map.items(): fill_helpers[key] = freeze_dict.get(val, '') fill_helpers['publish_value'] = 'no' if freeze_dict.get('PUBLISH', False): fill_helpers['publish_value'] = 'yes' # jquery fancy upload (add_import, add_init, add_ready) = fancy_upload_js(configuration, csrf_token=csrf_token) # We need filechooser deps and dynamic addition of copy/upload fields add_import += ''' <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.prettyprint.js"></script> <script type="text/javascript" src="/images/js/jquery.contextmenu.js"></script> <script type="text/javascript" src="/images/js/jquery.xbreadcrumbs.js"></script> <!-- The preview image plugin --> <script type="text/javascript" src="/images/js/preview.js"></script> <!-- The image manipulation CamanJS plugin used by the preview image plugin --> <script type="text/javascript" src="/images/lib/CamanJS/dist/caman.full.js"></script> <!-- The nouislider plugin used by the preview image plugin --> <script type="text/javascript" src="/images/lib/noUiSlider/jquery.nouislider.all.js"></script> ''' add_init += ''' Caman.DEBUG = false var copy_fields = 0; var upload_fields = 0; var open_file_chooser; var open_upload_dialog; /* default upload destination */ var remote_path = "%s"; var trash_linkname = "%s"; var copy_div_id = "copyfiles"; var upload_div_id = "uploadfiles"; /* for upload_callback */ var field_id, field_name, wrap_id, upload_path, on_remove; function add_copy() { var div_id = copy_div_id; var field_id = "freeze_copy_"+copy_fields; var field_name = "freeze_copy_"+copy_fields; var wrap_id = field_id+"_wrap"; var browse_id = field_id+"_browse"; copy_entry = "<span id=\'"+wrap_id+"\'>"; copy_entry += "<input type=\'button\' value=\'Remove\' "; copy_entry += " onClick=\'remove_field(\"+wrap_id+\");\'/>"; // add browse button to mimic upload field copy_entry += "<input type=\'button\' id=\'"+browse_id+"\' "; copy_entry += " value=\'Browse...\' />"; copy_entry += "<input type=\'text\' id=\'"+field_id+"\' "; copy_entry += " name=\'" + field_name + "\' size=80 /><br / >"; copy_entry += "</span>"; $("#"+div_id).append(copy_entry); $("#"+field_id).click(function() { open_file_chooser("Add file or directory (right click to select)", function(file) { $("#"+field_id).val(file); }); }); $("#"+browse_id).click(function() { $("#"+field_id).click(); }); $("#"+field_id).click(); copy_fields += 1; } function upload_callback() { var div_id = upload_div_id; console.log("in upload callback"); $(".uploadfileslist > tr > td > p.name > a").each( function(index) { console.log("callback for upload item no. "+index); upload_path = $(this).text(); if ($(this).attr("href") == "") { console.log("skipping empty (error) upload: "+upload_path); // Continue to next iteration on errors return true; } console.log("callback for upload path "+upload_path); field_id = "freeze_move_"+upload_fields; field_name = "freeze_move_"+upload_fields; wrap_id = field_id+"_wrap"; if ($("#"+div_id+" > span > input[value=\\""+upload_path+"\\"]").length) { console.log("skipping duplicate path: "+upload_path); // Continue to next iteration on errors return true; } else { console.log("adding new path: "+upload_path); } on_remove = ""; on_remove += "remove_field("+wrap_id+");"; on_remove += "$.fn.delete_upload(\\""+upload_path+"\\");"; upload_entry = "<span id=\'"+wrap_id+"\'>"; upload_entry += "<input type=\'button\' value=\'Remove\' "; upload_entry += " onClick=\'"+on_remove+"\'/>"; upload_entry += "<input type=\'text\' id=\'"+field_id+"\' "; upload_entry += " name=\'" + field_name + "\' size=50 "; upload_entry += "value=\\""+upload_path+"\\" /><br / >"; upload_entry += "</span>"; $("#"+div_id).append(upload_entry); console.log("callback added upload: "+upload_entry); upload_fields += 1; }); console.log("callback done"); } function add_upload() { openFancyUpload("Upload Files", upload_callback, "", remote_path, true, "", "%s"); } function remove_field(field_id) { $(field_id).remove(); } // init file chooser dialogs with directory selection support function init_dialogs() { open_file_chooser = mig_filechooser_init("fm_filechooser", function(file) { return; }, false, "/"); /* We reuse shared init function but use custom actual opener above to mangle resulting files into archive form. */ initFancyUpload(); /* Alias open_dialog to open_upload_dialog for clarity */ open_upload_dialog = open_dialog; $("#addfilebutton").click(function() { add_copy(); }); $("#adduploadbutton").click(function() { add_upload(); }); } function init_page() { init_dialogs(); } ''' % (upload_tmp_dir, trash_linkname, csrf_token) add_ready += ''' // do sequenced initialisation (separate function) init_page(); ''' # TODO: can we update style inline to avoid explicit themed_styles? title_entry['style'] = themed_styles( configuration, base=[ 'jquery.contextmenu.css', 'jquery.managers.contextmenu.css', 'jquery.xbreadcrumbs.css', 'jquery.fmbreadcrumbs.css', 'jquery.fileupload.css', 'jquery.fileupload-ui.css' ], skin=['fileupload-ui.custom.css', 'xbreadcrumbs.custom.css'], user_settings=title_entry.get('user_settings', {})) title_entry['script']['advanced'] += add_import title_entry['script']['init'] += add_init title_entry['script']['ready'] += add_ready if flavor == 'freeze': fill_helpers['freeze_name'] = fill_helpers.get('freeze_name', '') fill_helpers["archive_header"] = "Freeze Archive Files" fill_helpers["button_label"] = "Save and Preview" intro_text = """ Please enter your archive details below and select any files to be included in the archive. """ elif flavor == 'phd': fill_helpers['freeze_name'] = fill_helpers.get('freeze_name', '') fill_helpers[ "archive_header"] = "Thesis and Associated Files to Archive" fill_helpers["button_label"] = "Save and Preview" intro_text = """ Please enter your PhD details below and select any files associated with your thesis. """ elif flavor == 'backup': fill_helpers['freeze_name'] = fill_helpers.get('freeze_name', '') if not fill_helpers['freeze_name']: now = datetime.datetime.now().isoformat().replace(':', '') fill_helpers['freeze_name'] = 'backup-%s' % now fill_helpers["archive_header"] = "Files and folders to Archive" fill_helpers["button_label"] = "Save and Preview" intro_text = """ Please select the files and folders to backup below. """ else: output_objects.append({ 'object_type': 'error_text', 'text': "unknown flavor: %s" % flavor }) return (output_objects, returnvalues.SYSTEM_ERROR) # Only warn here if default state is final and persistent if freeze_flavors[flavor]['states'][0] == keyword_final and \ flavor in configuration.site_permanent_freeze: intro_text += """ <p class='warn_message'>Note that frozen archives <em>cannot</em> be changed after final creation and then they can only be manually removed by management, so please be careful when filling in the details. </p> """ fill_helpers["fancy_dialog"] = fancy_upload_html(configuration) files_form = """ <!-- and now this... we do not want to see it, except in a dialog: --> <div id='fm_filechooser' style='display:none'> <div class='tree-container container-fluid'> <div class='tree-row row'> <div class='tree-header col-3'></div> <div class='fm_path_breadcrumbs col-6'> <ul id='fm_xbreadcrumbs' class='xbreadcrumbs'><!-- dynamic --></ul> </div> <div class='fm_buttonbar col-3 d-none d-lg-block'> <ul id='fm_buttons' class='buttonbar'> <!-- dynamically modified by js to show optional buttons --> <li class='datatransfersbutton hidden' title='Manage Data Transfers'> </li> <li class='sharelinksbutton hidden' title='Manage Share Links'> </li> <li class='parentdirbutton' title='Open Parent Directory'> </li> <li class='refreshbutton' title='Refresh'> </li> </ul> </div> <div class='fm_buttonbar-big col-6 d-block d-lg-none hidden'> <ul id='fm_buttons' class='buttonbar'> <!-- dynamically modified by js to show optional buttons --> <li class='datatransfersbutton hidden' title='Manage Data Transfers'> </li> <li class='sharelinksbutton hidden' title='Manage Share Links'> </li> <li class='parentdirbutton' title='Open Parent Directory'> </li> <li class='refreshbutton' title='Refresh'> </li> </ul> </div> </div> </div> <div class='fm_addressbar'> <input type='hidden' value='/' name='fm_current_path' /> </div> <div class='fm_folders'> <ul class='jqueryFileTree'> <li class='directory expanded'> <a>...</a> </li> </ul> </div> <div class='fm_files'> <table id='fm_filelisting' style='font-size:13px; border-spacing=0;'> <thead> <tr> <th class='fm_name'>Name</th> <th class='fm_size'>Size</th> <th class='fm_type'>Type</th> <th class='fm_date'>Date Modified</th> <th class='fm_toolbox'>...</th> </tr> </thead> <tbody> <!-- this is a placeholder for contents: do not remove! --> </tbody> </table> </div> <div id='fm_statusbar' class='col-lg-12'> <div id='fm_statusprogress' class=' col-lg-3'> <div class='progress-label'>Loading...</div> </div> <div id='fm_statusinfo' class='col-lg-9'> </div> </div> </div> <div id='cmd_dialog' title='Command output' style='display: none;'></div> %(fancy_dialog)s """ % fill_helpers target_op = 'createfreeze' csrf_token = make_csrf_token(configuration, form_method, target_op, client_id, csrf_limit) fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token}) freeze_form = """ <form enctype='multipart/form-data' method='%(form_method)s' action='%(target_op)s.py'> <input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' /> <input type='hidden' name='flavor' value='%(flavor)s' /> <input type='hidden' name='freeze_id' value='%(freeze_id)s' /> <b>Name:</b><br /> <input class='fillwidth padspace' type='text' name='freeze_name' value='%(freeze_name)s' autofocus required pattern='[a-zA-Z0-9_. -]+' title='unique name for the freeze archive. I.e. letters and digits separated only by spaces, underscores, periods and hyphens' /> """ if flavor != 'backup': # TODO: do these make sense to have here or just forced in backend? freeze_form += """ <input type='hidden' name='freeze_department' value='' /> <input type='hidden' name='freeze_organization' value='' /> <br><b>Author(s):</b><br /> <input class='fillwidth padspace' type='text' name='freeze_author' value='%(freeze_author)s' title='optional archive author(s) in case archive is created on behalf of one or more people' /> <br /><b>Description:</b><br /> <textarea class='fillwidth padspace' rows='20' name='freeze_description'>%(freeze_description)s</textarea> <br /> """ freeze_form += """ <br /> <div id='freezefiles'> <b>%(archive_header)s:</b><br/> """ freeze_form += """ <input type='button' id='addfilebutton' value='Add file/directory' /> """ if flavor != 'backup': freeze_form += """ <input type='button' id='adduploadbutton' value='Add upload' /> """ freeze_form += """ <div id='copyfiles'> <!-- Dynamically filled --> </div> """ if flavor != 'backup': freeze_form += """ <div id='uploadfiles'> <!-- Dynamically filled --> </div> """ freeze_form += """ </div> <br /> """ if flavor != 'backup': freeze_form += """ <div id='freezepublish'> <b>Make Dataset Publicly Available</b> """ for val in ('yes', 'no'): checked = '' if fill_helpers['publish_value'] == val: checked = 'checked="checked"' freeze_form += """ <input type='radio' name='freeze_publish' value='%s' %s />%s """ % (val, checked, val) freeze_form += """ </div> <br /> """ freeze_form += """ <input type='submit' value='%(button_label)s' /> </form> """ # TODO: consider in-lining showfreeze file table for direct modify instead # probably requires more AJAX handling of actions. # for rel_path in frozen_files: # freeze_form += """%s<br/>""" % rel_path output_objects.append({'object_type': 'html_form', 'text': intro_text}) output_objects.append({'object_type': 'html_form', 'text': files_form}) output_objects.append({ 'object_type': 'html_form', 'text': freeze_form % fill_helpers }) # Link to view if archive already exists if freeze_id != keyword_auto: frozen_files = [ i['name'] for i in freeze_dict.get('FILES', []) if i['name'] != public_archive_index ] # Info about contents and spacing before view button otherwise if frozen_files: output_objects.append({ 'object_type': 'html_form', 'text': """ <h3>Previously Added Files</h3> <p>There are already %d file(s) saved in the archive and you can view and manage those through the link below e.g. in case you change your mind about including any of them. </p>""" % len(frozen_files) }) else: output_objects.append({'object_type': 'text', 'text': ''}) output_objects.append({ 'object_type': 'link', 'destination': 'showfreeze.py?freeze_id=%s;flavor=%s' % (freeze_id, flavor), 'class': 'viewarchivelink iconspace genericbutton', 'title': 'View details about your %s archive' % flavor, 'text': 'View details', 'target': '_blank', }) # Spacing output_objects.append({ 'object_type': 'html_form', 'text': ''' <div class="vertical-spacer"></div> ''' }) return (output_objects, returnvalues.OK)
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = \ initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False, ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) flavor = accepted['flavor'][-1].strip() if not flavor in freeze_flavors.keys(): output_objects.append({'object_type': 'error_text', 'text': 'Invalid freeze flavor: %s' % flavor}) return (output_objects, returnvalues.CLIENT_ERROR) title = freeze_flavors[flavor]['adminfreeze_title'] output_objects.append({'object_type': 'header', 'text': title}) title_entry = find_entry(output_objects, 'title') title_entry['text'] = title if not configuration.site_enable_freeze: output_objects.append({'object_type': 'text', 'text': '''Freezing archives is disabled on this site. Please contact the Grid admins %s if you think it should be enabled. ''' % configuration.admin_email}) return (output_objects, returnvalues.OK) # jquery support for dynamic addition of copy/upload fields title_entry['style'] = themed_styles(configuration, base=['jquery.contextmenu.css', 'jquery.xbreadcrumbs.css', 'jquery.fmbreadcrumbs.css', 'jquery.fileupload.css', 'jquery.fileupload-ui.css'], skin=['fileupload-ui.custom.css', 'xbreadcrumbs.custom.css']) title_entry['javascript'] = ''' <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <!-- Filemanager and dependencies --> <script type="text/javascript" src="/images/js/jquery.form.js"></script> <script type="text/javascript" src="/images/js/jquery.prettyprint.js"></script> <script type="text/javascript" src="/images/js/jquery.filemanager.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery.contextmenu.js"></script> <script type="text/javascript" src="/images/js/jquery.xbreadcrumbs.js"></script> <!-- The preview image plugin --> <script type="text/javascript" src="/images/js/preview.js"></script> <!-- The image manipulation CamanJS plugin used by the preview image plugin --> <script type="text/javascript" src="/images/lib/CamanJS/dist/caman.full.js"></script> <script type="text/javascript"> Caman.DEBUG = false </script> <!-- The nouislider plugin used by the preview image plugin --> <script type="text/javascript" src="/images/lib/noUiSlider/jquery.nouislider.all.js"></script> <!-- Fancy file uploader and dependencies --> <!-- The Templates plugin is included to render the upload/download listings --> <script type="text/javascript" src="/images/js/tmpl.min.js"></script> <!-- The Load Image plugin is included for the preview images and image resizing functionality --> <script type="text/javascript" src="/images/js/load-image.min.js"></script> <!-- Bootstrap JS is not required, but included for the responsive demo navigation --> <!-- The Iframe Transport is required for browsers without support for XHR file uploads --> <script type="text/javascript" src="/images/js/jquery.iframe-transport.js"></script> <!-- The basic File Upload plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload.js"></script> <!-- The File Upload processing plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-process.js"></script> <!-- The File Upload image preview & resize plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-image.js"></script> <!-- The File Upload validation plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-validate.js"></script> <!-- The File Upload user interface plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-ui.js"></script> <!-- The File Upload jQuery UI plugin --> <script type="text/javascript" src="/images/js/jquery.fileupload-jquery-ui.js"></script> <!-- The template to display files available for upload --> <script id="template-upload" type="text/x-tmpl"> {% console.log("using upload template"); %} {% console.log("... with upload files: "+$.fn.dump(o)); %} {% var dest_dir = "./" + $("#fancyfileuploaddest").val(); %} {% console.log("using upload dest: "+dest_dir); %} {% for (var i=0, file; file=o.files[i]; i++) { %} {% var rel_path = $.fn.normalizePath(dest_dir+"/"+file.name); %} {% console.log("using upload rel_path: "+rel_path); %} <tr class="template-upload fade"> <td> <span class="preview"></span> </td> <td> <p class="name">{%=rel_path%}</p> <strong class="error"></strong> </td> <td> <div class="size pending">Processing...</div> <div class="progress"></div> </td> <td> {% if (!i && !o.options.autoUpload) { %} <button class="start" disabled>Start</button> {% } %} {% if (!i) { %} <button class="cancel">Cancel</button> {% } %} </td> </tr> {% } %} </script> <!-- The template to display files available for download --> <script id="template-download" type="text/x-tmpl"> {% console.log("using download template"); %} {% console.log("... with download files: "+$.fn.dump(o)); %} {% for (var i=0, file; file=o.files[i]; i++) { %} {% var rel_path = $.fn.normalizePath("./"+file.name); %} {% console.log("using download rel_path: "+rel_path); %} <tr class="template-download fade"> <td> <span class="preview"> {% if (file.thumbnailUrl) { %} <a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" data-gallery><img src="{%=file.thumbnailUrl%}"></a> {% } %} </span> </td> <td> <p class="name"> <a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" {%=file.thumbnailUrl?\'data-gallery\':\'\'%}>{%=rel_path%}</a> </p> {% if (file.error) { %} <div><span class="error">Error</span> {%=file.error%}</div> {% } %} </td> <td> <div class="size">{%=o.formatFileSize(file.size)%}</div> </td> <td> <button class="delete" data-type="{%=file.deleteType%}" data-url="{%=file.deleteUrl%}"{% if (file.deleteWithCredentials) { %} data-xhr-fields=\'{"withCredentials":true}\'{% } %}>{% if (file.deleteUrl) { %}Delete{% } else { %}Dismiss{% } %}</button> <input type="checkbox" name="delete" value="1" class="toggle"> </td> </tr> {% } %} </script> <script type="text/javascript" > var copy_fields = 0; var upload_fields = 0; var open_file_chooser; var open_upload_dialog; ''' title_entry['javascript'] += ''' /* default upload destination */ var remote_path = "%s"; ''' % upload_tmp_dir title_entry['javascript'] += ''' function add_copy(div_id) { var field_id = "freeze_copy_"+copy_fields; var field_name = "freeze_copy_"+copy_fields; var wrap_id = field_id+"_wrap"; var browse_id = field_id+"_browse"; copy_entry = "<span id=\'"+wrap_id+"\'>"; copy_entry += "<input type=\'button\' value=\'Remove\' "; copy_entry += " onClick=\'remove_field(\"+wrap_id+\");\'/>"; // add browse button to mimic upload field copy_entry += "<input type=\'button\' id=\'"+browse_id+"\' "; copy_entry += " value=\'Browse...\' />"; copy_entry += "<input type=\'text\' id=\'"+field_id+"\' "; copy_entry += " name=\'" + field_name + "\' size=80 /><br / >"; copy_entry += "</span>"; $("#"+div_id).append(copy_entry); $("#"+field_id).click(function() { open_file_chooser("Add file or directory (right click to select)", function(file) { $("#"+field_id).val(file); }); }); $("#"+browse_id).click(function() { $("#"+field_id).click(); }); $("#"+field_id).click(); copy_fields += 1; } function add_upload(div_id) { var field_id, field_name, wrap_id, path, on_remove; open_upload_dialog("Upload Files", function() { console.log("in upload callback"); $(".uploadfileslist > tr > td > p.name > a").each( function(index) { console.log("callback for upload item no. "+index); path = $(this).text(); if ($(this).attr("href") == "") { console.log("skipping empty (error) upload: "+path); // Continue to next iteration on errors return true; } console.log("callback for upload path "+path); field_id = "freeze_move_"+upload_fields; field_name = "freeze_move_"+upload_fields; wrap_id = field_id+"_wrap"; if ($("#"+div_id+" > span > input[value=\'"+path+"\']").length) { console.log("skipping duplicate path: "+path); // Continue to next iteration on errors return true; } else { console.log("adding new path: "+path); } on_remove = ""; on_remove += "remove_field("+wrap_id+");"; on_remove += "$.fn.delete_upload(\\""+path+"\\");"; upload_entry = "<span id=\'"+wrap_id+"\'>"; upload_entry += "<input type=\'button\' value=\'Remove\' "; upload_entry += " onClick=\'"+on_remove+"\'/>"; upload_entry += "<input type=\'text\' id=\'"+field_id+"\' "; upload_entry += " name=\'" + field_name + "\' size=50 "; upload_entry += "value=\'"+path+"\' /><br / >"; upload_entry += "</span>"; $("#"+div_id).append(upload_entry); console.log("callback added upload: "+upload_entry); upload_fields += 1; }); console.log("callback done"); }, remote_path, true); } function remove_field(field_id) { $(field_id).remove(); } // init file chooser dialogs with directory selction support function init_dialogs() { open_file_chooser = mig_filechooser_init("fm_filechooser", function(file) { return; }, false, "/"); open_upload_dialog = mig_fancyuploadchunked_init("fancyuploadchunked_dialog"); $("#addfilebutton").click(function() { add_copy(\"copyfiles\"); }); $("#adduploadbutton").click(function() { add_upload(\"uploadfiles\"); }); } function init_page() { init_dialogs(); } $(document).ready(function() { // do sequenced initialisation (separate function) init_page(); } ); </script> ''' shared_files_form = """ <!-- and now this... we do not want to see it, except in a dialog: --> <div id='fm_filechooser' style='display:none'> <div class='fm_path_breadcrumbs'> <ul id='fm_xbreadcrumbs' class='xbreadcrumbs'> </ul> </div> <div class='fm_addressbar'> <input type='hidden' value='/' name='fm_current_path' /> </div> <div class='fm_folders'> <ul class='jqueryFileTree'> <li class='directory expanded'> <a>...</a> </li> </ul> </div> <div class='fm_files'> <table id='fm_filelisting' style='font-size:13px; border-spacing=0;'> <thead> <tr> <th>Name</th> <th style='width: 80px;'>Size</th> <th style='width: 50px;'>Type</th> <th style='width: 120px;'>Date Modified</th> </tr> </thead> <tbody> <!-- this is a placeholder for contents: do not remove! --> </tbody> </table> </div> <div id='fm_statusbar'> <div id='fm_statusprogress'><div class='progress-label'>Loading...</div></div> <div id='fm_statusinfo'> </div> </div> <div id='fm_options'><input id='fm_touchscreen' type='checkbox'> Enable touch screen interface (all clicks trigger menu) <input id='fm_dotfiles' type='checkbox'> Show hidden files and dirs </div> </div> <div id='cmd_dialog' title='Command output' style='display: none;'></div> <div id='fancyuploadchunked_dialog' title='Upload File' style='display: none;'> <!-- The file upload form used as target for the file upload widget --> <form id='fancyfileupload' action='uploadchunked.py?output_format=json;action=put' method='POST' enctype='multipart/form-data'> <fieldset id='fancyfileuploaddestbox'> <label id='fancyfileuploaddestlabel' for='fancyfileuploaddest'> Optional final destination dir: </label> <input id='fancyfileuploaddest' type='text' size=60 value=''> </fieldset> <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload --> <div class='fileupload-buttonbar'> <div class='fileupload-buttons'> <!-- The fileinput-button span is used to style the file input field as button --> <span class='fileinput-button'> <span>Add files...</span> <input type='file' name='files[]' multiple> </span> <button type='submit' class='start'>Start upload</button> <button type='reset' class='cancel'>Cancel upload</button> <button type='button' class='delete'>Delete</button> <input type='checkbox' class='toggle'> <!-- The global file processing state --> <span class='fileupload-process'></span> </div> <!-- The global progress state --> <div class='fileupload-progress fade' style='display:none'> <!-- The global progress bar --> <div class='progress' role='progressbar' aria-valuemin='0' aria-valuemax='100'></div> <!-- The extended global progress state --> <div class='progress-extended'> </div> </div> </div> <!-- The table listing the files available for upload/download --> <table role='presentation' class='table table-striped'><tbody class='uploadfileslist'></tbody></table> </form> <!-- For status and error output messages --> <div id='fancyuploadchunked_output'></div> </div> """ if flavor == 'freeze': intro_text = """ Please enter your archive details below and select any files to be included in the archive. <p class='warn_message'>Note that a frozen archive can not be changed after creation and it can only be manually removed by the management, so please be careful when filling in the details. </p> """ files_form = shared_files_form freeze_form = """ <form enctype='multipart/form-data' method='post' action='createfreeze.py'> <b>Name:</b><br /> <input type='hidden' name='flavor' value='freeze' /> <input type='text' name='freeze_name' size=30 autofocus /> <input type='hidden' name='freeze_author' value='UNSET' /> <input type='hidden' name='freeze_department' value='UNSET' /> <input type='hidden' name='freeze_organization' value='UNSET' /> <br /><b>Description:</b><br /> <textarea cols='80' rows='20' name='freeze_description'></textarea> <br /> <br /> <div id='freezefiles'> <b>Freeze Archive Files:</b> <input type='button' id='addfilebutton' value='Add file/directory' /> <input type='button' id='adduploadbutton' value='Add upload' /> <div id='copyfiles'> <!-- Dynamically filled --> </div> <div id='uploadfiles'> <!-- Dynamically filled --> </div> </div> <br /> <div id='freezepublish'> <input type='checkbox' name='freeze_publish' /> <b>Make Dataset Publicly Available</b> </div> <br /> <input type='submit' value='Create Archive' /> </form> """ if flavor == 'phd': intro_text = """ Please enter your PhD details below and select any files associated with your thesis. <p class='warn_message'>Note that a thesis archive can not be changed after creation and it can only be manually removed by the management, so please be careful when filling in the details. </p> """ files_form = shared_files_form freeze_form = """ <form enctype='multipart/form-data' method='post' action='createfreeze.py'> <b>Thesis Title:</b><br /> <input type='hidden' name='flavor' value='phd' /> <input type='hidden' name='freeze_organization' value='UNSET' /> <input type='text' name='freeze_name' size=80 /> <br /><b>Author Name:</b><br /> <input type='text' name='freeze_author' size=40 /> <br /><b>Department:</b><br /> <input type='text' name='freeze_department' size=40 /> <br /> <br /> <div id='freezefiles'> <b>Thesis and Associated Files to Archive:</b> <input type='button' id='addfilebutton' value='Add file/directory' /> <input type='button' id='adduploadbutton' value='Add upload' /> <div id='copyfiles'> <!-- Dynamically filled --> </div> <div id='uploadfiles'> <!-- Dynamically filled --> </div> </div> <br /> <div id='freezepublish'> <input type='checkbox' name='freeze_publish' /> <b>Make Dataset Publicly Available</b> </div> <br /><b>Dataset Description:</b><br /> <textarea cols='80' rows='20' name='freeze_description'></textarea> <br /> <br /> <input type='submit' value='Archive Thesis' /> </form> """ output_objects.append({'object_type': 'html_form', 'text': intro_text}) output_objects.append({'object_type': 'html_form', 'text': files_form}) output_objects.append({'object_type': 'html_form', 'text': freeze_form}) return (output_objects, returnvalues.OK)
def css_tmpl(configuration): """Stylesheets to include in the page header""" css = themed_styles(configuration, base=['jquery.contextmenu.css']) return css
def main(client_id, user_arguments_dict): """Main function used by front end""" (configuration, logger, output_objects, op_name) = initialize_main_variables(client_id, op_header=False) defaults = signature()[1] (validate_status, accepted) = validate_input_and_cert( user_arguments_dict, defaults, output_objects, client_id, configuration, allow_rejects=False ) if not validate_status: return (accepted, returnvalues.CLIENT_ERROR) flavor = accepted["flavor"][-1] if not flavor in freeze_flavors.keys(): output_objects.append({"object_type": "error_text", "text": "Invalid freeze flavor: %s" % flavor}) return (output_objects, returnvalues.CLIENT_ERROR) title = freeze_flavors[flavor]["showfreeze_title"] output_objects.append({"object_type": "header", "text": title}) title_entry = find_entry(output_objects, "title") title_entry["text"] = title if not configuration.site_enable_freeze: output_objects.append( { "object_type": "text", "text": """Freezing archives is disabled on this site. Please contact the Grid admins %s if you think it should be enabled. """ % configuration.admin_email, } ) return (output_objects, returnvalues.OK) # jquery support for tablesorter css_helpers = { "css_base": os.path.join(configuration.site_images, "css"), "skin_base": configuration.site_skin_base, } title_entry["style"] = themed_styles(configuration) title_entry["javascript"] = ( """ <script type="text/javascript" src="/images/js/jquery.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.pager.js"></script> <script type="text/javascript" src="/images/js/jquery.tablesorter.widgets.js"></script> <script type="text/javascript" src="/images/js/jquery-ui.js"></script> <script type="text/javascript" > $(document).ready(function() { // table initially sorted by col. 0 (filename) var sortOrder = [[0,1]]; // use image path for sorting if there is any inside var imgTitle = function(contents) { var key = $(contents).find("a").attr("class"); if (key == null) { key = $(contents).html(); } return key; } $("#frozenfilestable").tablesorter({widgets: ["zebra", "saveSort"], sortList:sortOrder, textExtraction: imgTitle }) .tablesorterPager({ container: $("#pager"), size: %s }); } ); </script> """ % default_pager_entries ) freeze_id = accepted["freeze_id"][-1] # NB: the restrictions on freeze_id prevents illegal directory traversal if not is_frozen_archive(freeze_id, configuration): logger.error("%s: invalid freeze '%s': %s" % (op_name, client_id, freeze_id)) output_objects.append( {"object_type": "error_text", "text": "'%s' is not an existing frozen archive!" % freeze_id} ) return (output_objects, returnvalues.CLIENT_ERROR) (load_status, freeze_dict) = get_frozen_archive(freeze_id, configuration) if not load_status: logger.error("%s: load failed for '%s': %s" % (op_name, freeze_id, freeze_dict)) output_objects.append({"object_type": "error_text", "text": 'Could not read details for "%s"' % freeze_id}) return (output_objects, returnvalues.SYSTEM_ERROR) if freeze_dict.get("FLAVOR", "freeze") != flavor: logger.error("%s: flavor mismatch for '%s': %s vs %s" % (op_name, freeze_id, flavor, freeze_dict)) output_objects.append({"object_type": "error_text", "text": 'No such %s archive "%s"' % (flavor, freeze_id)}) return (output_objects, returnvalues.CLIENT_ERROR) output_objects.append( {"object_type": "table_pager", "entry_name": "frozen files", "default_entries": default_pager_entries} ) output_objects.append(build_freezeitem_object(configuration, freeze_dict)) return (output_objects, returnvalues.OK)