blob: 39eb67fca385aa29c25483d5e3f062c1ceaaae24 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.uima.ducc.ws.server.nodeviz;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.uima.ducc.common.Node;
import org.apache.uima.ducc.common.NodeConfiguration;
import org.apache.uima.ducc.common.SizeBytes;
import org.apache.uima.ducc.common.utils.DuccLogger;
import org.apache.uima.ducc.common.utils.DuccLoggerComponents;
import org.apache.uima.ducc.common.utils.DuccPropertiesResolver;
import org.apache.uima.ducc.common.utils.SystemPropertyResolver;
import org.apache.uima.ducc.common.utils.Version;
import org.apache.uima.ducc.transport.event.OrchestratorStateDuccEvent;
import org.apache.uima.ducc.transport.event.common.IDuccProcess;
import org.apache.uima.ducc.transport.event.common.IDuccProcessMap;
import org.apache.uima.ducc.transport.event.common.IDuccReservation;
import org.apache.uima.ducc.transport.event.common.IDuccReservationMap;
import org.apache.uima.ducc.transport.event.common.IDuccSchedulingInfo;
import org.apache.uima.ducc.transport.event.common.IDuccStandardInfo;
import org.apache.uima.ducc.transport.event.common.IDuccTypes.DuccType;
import org.apache.uima.ducc.transport.event.common.IDuccWork;
import org.apache.uima.ducc.transport.event.common.IDuccWorkExecutable;
import org.apache.uima.ducc.transport.event.common.IDuccWorkMap;
import org.apache.uima.ducc.transport.event.common.IDuccWorkReservation;
import org.apache.uima.ducc.transport.event.common.IDuccWorkService;
import org.apache.uima.ducc.transport.event.common.IDuccWorkService.ServiceDeploymentType;
import org.apache.uima.ducc.transport.event.common.IProcessState.ProcessState;
import org.apache.uima.ducc.ws.DuccMachinesData;
import org.apache.uima.ducc.ws.IListenerOrchestrator;
import org.apache.uima.ducc.ws.MachineInfo;
import org.apache.uima.ducc.ws.server.DuccListeners;
import org.apache.uima.ducc.ws.types.NodeId;
public class NodeViz
implements IListenerOrchestrator
{
private static DuccLogger logger = DuccLoggerComponents.getWsLogger(NodeViz.class.getName());
private DuccMachinesData machineData; // handle to static machine information
//private long lastUpdate = 0; // time of last orchestrator state
private String visualization; // cached visualization
private long update_interval = 60000; // Only gen a new viz every 'this long'
static int default_quantum = 4; // for hosts with no jobs so we don't know or care that much
private String version = "1.1.0";
static String wshost = "";
static String wsport = "42133";
static boolean strip_domain = true;
public NodeViz()
{
String methodName = "NodeViz";
update_interval = SystemPropertyResolver.getLongProperty("ducc.viz.update.interval", update_interval);
default_quantum = SystemPropertyResolver.getIntProperty("ducc.rm.share.quantum", default_quantum);
wshost = SystemPropertyResolver.getStringProperty("ducc.ws.node", System.getProperty("ducc.head"));
wsport = SystemPropertyResolver.getStringProperty("ducc.ws.port", wsport);
strip_domain = SystemPropertyResolver.getBooleanProperty("ducc.ws.visualization.strip.domain", true);
logger.info(methodName, null, "------------------------------------------------------------------------------------");
logger.info(methodName, null, "Node Visualization starting:");
logger.info(methodName, null, " DUCC home : ", System.getProperty("DUCC_HOME"));
logger.info(methodName, null, " ActiveMQ URL : ", System.getProperty("ducc.broker.url"));
logger.info(methodName, null, "Default Quantum : ", default_quantum);
logger.info(methodName, null, "Viz update Interval : ", update_interval);
logger.info(methodName, null, "Web Server Host : ", wshost);
logger.info(methodName, null, "Web Server Port : ", wsport);
logger.info(methodName, null, "Strip Domains : ", strip_domain);
logger.info(methodName, null, "");
logger.info(methodName, null, " JVM : ", System.getProperty("java.vendor") +
" "+ System.getProperty("java.version"));
logger.info(methodName, null, " JAVA_HOME : ", System.getProperty("java.home"));
logger.info(methodName, null, " JVM Path : ", System.getProperty("ducc.jvm"));
logger.info(methodName, null, " JMX URL : ", System.getProperty("ducc.jmx.url"));
logger.info(methodName, null, "");
logger.info(methodName, null, " OS Architecture : ", System.getProperty("os.arch"));
logger.info(methodName, null, "");
logger.info(methodName, null, " DUCC Version : ", Version.version());
logger.info(methodName, null, " Vizualization Version : ", version);
logger.info(methodName, null, "------------------------------------------------------------------------------------");
DuccListeners.getInstance().register(this);
machineData = DuccMachinesData.getInstance();
visualization = "<html><p>Waiting for node updates ...</p></html>";
}
public String getVisualization()
{
String methodName = "getVisualization";
logger.debug(methodName, null, "Request for visualization");
return visualization;
}
public void generateVisualization(OrchestratorStateDuccEvent ev)
{
String methodName = "generateVisualization";
Map<String, VisualizedHost> hosts = new HashMap<String, VisualizedHost>();
IDuccWorkMap jobmap = ev.getWorkMap();
int job_gb = 0;
int service_gb = 0;
int pop_gb = 0;
int reservation_gb = 0;
// Must find nost configuration so we can work out the quantum used to schedule each job
String class_definitions = SystemPropertyResolver
.getStringProperty(DuccPropertiesResolver
.ducc_rm_class_definitions, "scheduler.classes");
String user_registry = SystemPropertyResolver
.getStringProperty(DuccPropertiesResolver
.ducc_rm_user_registry, "ducc.users");
class_definitions = System.getProperty("DUCC_HOME") + "/resources/" + class_definitions;
NodeConfiguration nc = new NodeConfiguration(class_definitions, null, user_registry, logger); // UIMA-4142 make the config global
try {
nc.readConfiguration();
} catch (Exception e ) {
logger.error(methodName, null, "Cannot read node configuration. Some information may not be quite right.");
}
// first step, generate the viz from the OR map which seems to have everything we need
// next stop, walk the machines list and generate empty node for any machine in that list
// that had no work on it
// lext step, walk the machines data and overlay a graphic for any node that is 'down' or 'defined'
// move 'down' hosts with jobs on them to the front
// move all onther 'not up' hosts to the end
// finally, walk the list and make them render
for ( Object o : jobmap.values() ) {
IDuccWork w = (IDuccWork) o;
DuccType type = w.getDuccType();
String service_endpoint = null;
String service_id = null; // UIMA-4209
// If it looks service-y and os of deployment type 'other' it's a Pop.
if ( type == DuccType.Service ) {
IDuccWorkService dws = (IDuccWorkService) w;
if ( dws.getServiceDeploymentType() == ServiceDeploymentType.other) {
type = DuccType.Pop;
} else {
service_endpoint = dws.getServiceEndpoint();
service_id = dws.getServiceId(); // UIMA-4209
}
}
if ( ! w.isSchedulable() ) {
logger.debug(methodName, w.getDuccId(), "Ignoring unschedulable work:", w.getDuccType(), ":", w.getStateObject());
continue;
}
IDuccStandardInfo si = w.getStandardInfo();
IDuccSchedulingInfo sti = w.getSchedulingInfo();
String user = si.getUser();
String duccid = service_id == null ? Long.toString(w.getDuccId().getFriendly()) : service_id; // UIMA-4209
int jobmem = (int) (sti.getMemorySizeAllocatedInBytes()/SizeBytes.GB);
String sclass = sti.getSchedulingClass();
int quantum = default_quantum;
try {
quantum = nc.getQuantumForClass(sclass);
} catch ( Exception e ) {
// this most likely caused by a reconfigure so that a job's class no longer exists. nothing to do about it
// but punt and try not to crash.
logger.warn(methodName, null, "Cannot find scheduling class or quantum for " + sclass + ". Using default quantum of " + default_quantum);
}
int qshares = jobmem / quantum;
if ( jobmem % quantum != 0 ) qshares++;
switch ( type ) {
case Job:
case Pop:
case Service:
{
IDuccWorkExecutable de = (IDuccWorkExecutable) w;
IDuccProcessMap pm = de.getProcessMap();
logger.debug(methodName, w.getDuccId(), "Receive:", type, w.getStateObject(), "processes[", pm.size() + "]");
for ( IDuccProcess proc : pm.values() ) {
String pid = proc.getPID();
ProcessState state = proc.getProcessState();
Node n = proc.getNode();
logger.debug(methodName, w.getDuccId(), (n == null ? "N/A" : n.getNodeIdentity().getName()), "Process[", pid, "] state [", state, "] is complete[", proc.isComplete(), "]");
if ( proc.isComplete() ) {
continue;
}
switch ( type ) {
case Job:
job_gb += jobmem;
break;
case Pop:
pop_gb += jobmem;
break;
case Service:
service_gb += jobmem;
break;
default:
break;
}
if ( n != null ) {
String key = strip(n.getNodeIdentity().getName());
VisualizedHost vh = hosts.get(key);
if ( vh == null ) {
// System.out.println("Set host from OR with key:" + key + ":");
vh = new VisualizedHost(n, quantum);
hosts.put(key, vh);
}
vh.addWork(type, user, duccid, jobmem, qshares, service_endpoint);
}
}
}
break;
case Reservation:
{
IDuccWorkReservation de = (IDuccWorkReservation) w;
IDuccReservationMap rm = de.getReservationMap();
logger.debug(methodName, w.getDuccId(), "Receive:", type, w.getStateObject(), "processes[", rm.size(), "] Completed:", w.isCompleted());
reservation_gb += jobmem;
for ( IDuccReservation r: rm.values()) {
Node n = r.getNode();
if ( n == null ) {
logger.debug(methodName, w.getDuccId(), "Node [N/A] mem[N/A");
} else if ( n.getNodeIdentity() == null ) {
logger.debug(methodName, w.getDuccId(), "NodeIdentity [N/A] mem[N/A");
} else {
String key = strip(n.getNodeIdentity().getName());
VisualizedHost vh = hosts.get(key);
if ( vh == null ) {
vh = new VisualizedHost(n, quantum);
hosts.put(key, vh);
// System.out.println("Set host from OR with key:" + key + ":");
}
vh.addWork(type, user, duccid, jobmem, qshares, null);
}
}
}
break;
default:
logger.warn(methodName, w.getDuccId(), "Received work of type ?", w.getDuccType());
break;
}
}
logger.debug(methodName, null, "Generateing visualizaiton");
Map<MachineInfo,NodeId> m = machineData.getMachines();
for (Entry<MachineInfo, NodeId> entry : m.entrySet()) {
//
// This is for hosts that have no work on them so they didn't come in the work map
//
MachineInfo mi = entry.getKey();
// NOTE: the map changes all the time so the value may be gone. This situation
// will be fixed one day but for now just forget the node, it will show up
// next time we get here.
if ( mi == null ) continue;
String s = mi.getName();
if ( !mi.getStatus().equals("up") ) continue; // filter non-up nodes
String key = strip(s); // our key, possibly with domain stripped
if ( ! hosts.containsKey(key) ) {
VisualizedHost vh = new VisualizedHost(mi, nc.getQuantumForNode(s));
hosts.put(key, vh);
}
}
int total_gb = 0;
Markup markup = new Markup();
VisualizedHost[] sorted = hosts.values().toArray(new VisualizedHost[hosts.size()]);
Arrays.sort(sorted, new HostSorter());
for ( VisualizedHost vh : sorted ) {
vh.toSvg(markup);
total_gb += vh.countRam();
}
String page = markup.close();
int unoccupied_gb = total_gb - (job_gb + pop_gb + service_gb + reservation_gb);
visualization =
"<html>" +
"<div id=\"viz-header\" style=\"text-align:center\">" +
"Sort By " +
"<i onclick=\"ducc_viz_node_sorter('size')\" id=\"ducc-viz-sort-size\" style=\"color:red\">Size </i>" +
"<i onclick=\"ducc_viz_node_sorter('name')\" id=\"ducc-viz-sort-name\"\">Name</i>" +
"</br>" +
"<b>Memory Available:</b> " + total_gb +
"GB, <b>In use: </b>" +
"<b><i>Jobs:</i></b> " + (job_gb) +
"GB, <b><i>Services:</i></b> " + (service_gb) +
"GB, <b><i>Managed Reservations:</i></b> " + (pop_gb) +
"GB, <b><i>Reservations:</i></b> " + (reservation_gb) +
"GB, <b><i>Unoccupied:</i></b> " + (unoccupied_gb) +
"GB</div>" +
"<br>" +
//"<div id=\"nodelist\" style=\"background-color:e5e5e5\">" +
"<div id=\"nodelist\" style=\"background-color:eeeeee;padding:3\">" +
page +
"</div>" +
"<script>" +
"ducc_viz_onreload();" +
"</script>" +
"</html>";
// logger.info(methodName, null, "Size of node visualization:", visualization.length());
hosts = null;
}
/**
* Possibly strip domain name from hostname
*/
static String strip(String n)
{
if ( strip_domain ) {
int ndx = n.indexOf(".");
if ( ndx >= 0) {
n = n.substring(0, ndx);
}
}
return n;
}
public void update(OrchestratorStateDuccEvent ev)
{
String methodName = "update";
logger.debug(methodName, null, "Received Orchestrator Event");
//long currentUpdate = System.currentTimeMillis();
// if ( currentUpdate - lastUpdate > update_interval ) {
if ( true ) { // for debug, never skip
generateVisualization(ev);
//lastUpdate = currentUpdate;
}
//else {
//logger.debug(methodName, null, "Skipping visualization");
//}
}
/**
* The contract:
* - when equals returns true, compare must return 0
* - when equals returns false, compare must return 1 or -1
*/
private static class HostSorter
implements Comparator<VisualizedHost>
{
public int compare(VisualizedHost h1, VisualizedHost h2)
{
if ( h1 == h2 ) return 0;
if ( h1.equals(h2) ) return 0;
if ( h2.mem == h1.mem ) return h1.name.compareTo(h2.name);
return h2.mem - h1.mem;
}
}
}