def request_index(request, request_id): """ Detail page on a specific request (i.e. DAGManJobId@<submit host>) by creating an SVG representation of the executing DAG. """ # Load the template. t = loader.get_template('monitor/request_svg.html') # Get the bb entries. dagManJobId, host = request_id.split('@', 1) (dataset, user, requestId, stages) = blackboard.getOSFEntry(dagManJobId) # Filter out all the spurious ones. # FIXME: Maybe do this a bit better, huh? hh = host + '#' stages = [s for s in stages if s.GlobalJobId.startswith(hh)] # Create the DOT graph. dot = pydot.Dot(type='digraph') # Keep track of node names vs node ids. nodes = {} # {node name: [node id1, node id2, ...]} for stage in stages: nodeId = '%d.%d' % (stage.ClusterId, stage.ProcId) # Remember that if a condor job is on hold, it will have an ExitCode = # None and a JobState = Exited. if(stage.JobState != 'Exited'): nodeLabel = '%s - %s' % (stage.DAGNodeName, stage.JobState) elif(stage.ExitCode == None): # The corresponding condor job was put on hold. nodeLabel = '%s - %s' % (stage.DAGNodeName, 'On Hold') else: nodeLabel = '%s - %s (%d)' % (stage.DAGNodeName, stage.JobState, stage.ExitCode) if(stage.JobState == 'Exited' and stage.ExitCode != 0): dot.add_node(pydot.Node(nodeId, shape='ellipse', label=nodeLabel, style="filled", fillcolor="red")) else: dot.add_node(pydot.Node(nodeId, shape='ellipse', label=nodeLabel)) if(nodes.has_key(stage.DAGNodeName)): nodes[stage.DAGNodeName].append(nodeId) else: nodes[stage.DAGNodeName] = [nodeId, ] # Now add the edges of the graph. for stage in stages: if(stage.DAGParentNodeNames): nodeId = '%d.%d' % (stage.ClusterId, stage.ProcId) parentNames = stage.DAGParentNodeNames.split(',') for parentName in parentNames: if(not nodes.has_key(parentName)): continue # We have three cases: # 1. node.Instances == parent.Instances: 1 to 1 # 2. node.Instances > parent.Instances: scatter # 3. node.Instances < parent.Instances: gather # We can treat 2. and 3. the same way. !. is different. if(len(nodes[stage.DAGNodeName]) == len(nodes[parentName])): # Case 1. # # FIXME: here we assume that ProcIds are the same for parent and child. # pClusterId = int(nodes[parentName][0].split('.')[0]) pId = '%d.%d' % (pClusterId, stage.ProcId) dot.add_edge(pydot.Edge(pId, nodeId)) else: # Cases 2. and 3. for parentId in nodes[parentName]: dot.add_edge(pydot.Edge(parentId, nodeId)) # Create the SVG graph. # This does a system call, which I would rather avoid. # rawSvg = dot.create(format='svg') # This works standalone but not in the web environment... dunno why :-( # cLength = ctypes.c_int(1) # cSvg = ctypes.pointer(ctypes.create_string_buffer(1)) # dotString = dot.to_string() # # gvc = libgvc.gvContext() # g = libgraph.agmemread(dotString) # # libgvc.gvLayout(gvc, g, "dot") # libgvc.gvRenderData(gvc, g, "svg", # ctypes.byref(cSvg), ctypes.byref(cLength)) # # libgvc.gvFreeLayout(gvc, g) # libgraph.agclose(g) # libgvc.gvFreeContext(gvc) # # rawSvg = ctypes.string_at(cSvg) # This works, as long as I convert the unicode to string. dotString = dot.to_string() rawSvg = grapher.create(str(dotString), 'dot', 'svg') # Remove the header. svg = rawSvg.split('\n')[6:] # Render the template and exit. c = Context({'dataset': dataset, 'user': user, 'requestId': requestId, 'svg': '\n'.join(svg)}) return(HttpResponse(t.render(c), mimetype='application/xhtml+xml'))
#!/usr/bin/env python import grapher dot = open('bcw.dot').read() print(grapher.create(dot, "dot", "svg"))