Index: master/public_html/buildsteps.html |
diff --git a/master/public_html/buildsteps.html b/master/public_html/buildsteps.html |
deleted file mode 100644 |
index 2e846fc15fff6304b413926ec02a93f2214802bb..0000000000000000000000000000000000000000 |
--- a/master/public_html/buildsteps.html |
+++ /dev/null |
@@ -1,601 +0,0 @@ |
-<html> |
- <head> |
- <title>Skia Buildstep Dashboard</title> |
- <link rel="icon" href="favicon.ico"> |
- <script language="JavaScript"> |
- "use strict"; |
- |
- var DEFAULT_MASTER_FILTER = "*"; |
- var DEFAULT_BUILDER_FILTER = "*"; |
- var DEFAULT_BUILDSTEP_FILTER = "*"; |
- var DEFAULT_TIME_PERIOD = 24*60*60; // 24 hours in seconds. |
- |
- // Column indices for grouping Graphite data. |
- var GROUP_NODE_BUILDER = 2; |
- var GROUP_NODE_BUILDSTEP = 3; |
- |
- // We hard-code this giant interval to be passed to Graphite's summarize |
- // function to ensure that the data ends up grouped into one big bucket, no |
- // matter how we set the time period. |
- var SUMMARIZE_INTERVAL = "10year"; |
- |
- // Timestamp of when we started loading data. Used to compute load time. |
- var loadStart = null; |
- |
- // Used to store information about what data is currently being loaded. |
- var currentlyLoadingData = null; |
- |
- // Metrics to load from Graphite. |
- // Takes the form: [[metric_name, merge_function], ...] |
- var metrics = [["success", "sum"], |
- ["failure", "sum"], |
- ["duration", "avg"]]; |
- |
- // Metrics to derive from the loaded metrics above. We display these in the |
- // table. Takes the form: [[metric_name, derivation_fn], ...], where |
- // derivation_fn is a function which takes as a parameter a dictionary |
- // containing values for all of the above metrics for a given buildStep and |
- // returns a value, which may be null. |
- var derivedMetrics = [ |
- ["Duration", function(stepData) { |
- if (stepData["duration"] == undefined) { |
- return null; |
- } |
- return stepData["duration"]; |
- }], |
- ["Failure Rate", function(stepData) { |
- if (stepData["success"] == undefined || |
- stepData["failure"] == undefined) { |
- return null; |
- } |
- var success = parseFloat(stepData["success"]); |
- var failure = parseFloat(stepData["failure"]); |
- var totalRuns = success + failure; |
- var failureRate = failure / totalRuns; |
- if (totalRuns <= 0) { |
- return null; |
- } |
- return failureRate; |
- }], |
- ["Total Runs", function(stepData) { |
- if (stepData["success"] == undefined || |
- stepData["failure"] == undefined) { |
- return null; |
- } |
- var success = parseFloat(stepData["success"]); |
- var failure = parseFloat(stepData["failure"]); |
- return success + failure; |
- }], |
- ]; |
- |
- // Use the second data column as the default sort index, since that typically |
- // indicates the most important column which isn't a label. |
- var defaultSortIndex = 1; |
- |
- /** |
- * Display text or HTML in the logging div. |
- * |
- * @param {string} msg The HTML or text to display. |
- */ |
- function setMessage(msg) { |
- console.log(msg); |
- document.getElementById("logging_div").innerHTML = msg; |
- } |
- |
- /** |
- * Function to call when starting to load data. Prints the given URL and |
- * stores some data for retrieval on loadingDone(). |
- * |
- * @param {string} url The URL that we're loading. |
- * @param {Object} data Arbitrary data to store.s |
- */ |
- function loadingStart(url, data) { |
- document.body.style.cursor = "wait"; |
- document.getElementById("buildstep_div").style.display = "none"; |
- document.getElementById("builder_div").style.display = "none"; |
- document.getElementById("load_data_button").disabled = true; |
- loadStart = new Date().getTime(); |
- setMessage("Loading data from " + url); |
- currentlyLoadingData = data; |
- } |
- |
- /** |
- * Function to call when done loading data. Returns any data stored on |
- * loadingStart(). |
- */ |
- function loadingDone() { |
- document.body.style.cursor = "auto"; |
- document.getElementById("buildstep_div").style.display = "block"; |
- document.getElementById("builder_div").style.display = "block"; |
- document.getElementById("load_data_button").disabled = false; |
- var message = ""; |
- if (loadStart) { |
- var elapsedSeconds = (new Date().getTime() - loadStart) / 1000; |
- message = "Loaded data in " + elapsedSeconds + " seconds."; |
- } |
- setMessage(message); |
- var retData = currentlyLoadingData; |
- currentlyLoadingData = null; |
- return retData; |
- } |
- |
- /** |
- * Load data from the given URL using JSONP. |
- * |
- * @param {string} url The URL from which to load data. |
- * @param {string} callbackName The name of the function to use as a callback. |
- */ |
- function loadJSONP(url, callbackName) { |
- var script = document.createElement("script"); |
- var join = "&"; |
- if (url.indexOf("?") < 0) { |
- join = "?"; |
- } |
- script.src = url + join + "jsonp=" + callbackName; |
- document.head.appendChild(script); |
- } |
- |
- /** |
- * Re-organize Graphite data into a dictionary. |
- * |
- * @param {Array<Object>} data Data to organize. |
- */ |
- function graphiteDataDict(data) { |
- var dataDict = {}; |
- for (var i = 0; i < data.length; ++i) { |
- var splitName = data[i]["target"].split("."); |
- var name = splitName.slice(0, splitName.length - 1).join("."); |
- var resultType = splitName[1]; |
- var datapoints = data[i]["datapoints"]; |
- var result = 0; |
- if (datapoints.length > 0) { |
- result = datapoints[datapoints.length - 1][0]; |
- } |
- if (result == null) { |
- result = 0; |
- } |
- if (!dataDict[name]) { |
- dataDict[name] = {}; |
- } |
- dataDict[name][resultType] = result; |
- dataDict[name]["name"] = name; |
- } |
- return dataDict; |
- } |
- |
- /** |
- * Create lists of column names and rows given a data dictionary. |
- * |
- * @param {Object} dataDict data to organize into columns and rows. |
- */ |
- function getColsAndRows(dataDict) { |
- // Create the set of columns |
- var cols = ["Name"]; |
- for (var i = 0; i < derivedMetrics.length; ++i) { |
- cols.push(derivedMetrics[i][0]); |
- } |
- |
- // Create a row of data for each buildStep. |
- var rows = []; |
- for (var name in dataDict) { |
- var row = [name]; |
- // Fill in the data row. |
- for (var i = 0; i < derivedMetrics.length; ++i) { |
- var value = derivedMetrics[i][1](dataDict[name]); |
- row.push(value); |
- } |
- rows.push(row); |
- } |
- return [cols, rows] |
- } |
- |
- /** |
- * Build the data table given a set of data. |
- * |
- * @param {Array<Object>} data The data to put into the table. |
- * @param {string} title The title of the table. |
- * @param {string} containerId ID of the container to hold the table. |
- */ |
- function makeTableFromData(data, title, containerId) { |
- var buildStepData = graphiteDataDict(data); |
- var colsAndRows = getColsAndRows(buildStepData); |
- rebuildTable(title, colsAndRows[0], colsAndRows[1], containerId, |
- null, true); |
- } |
- |
- /** |
- * Sort the rows on the given column index. |
- * |
- * @param {Array<Array>} rows The rows to sort. |
- * @param {number} sortIndex The index of the column by which to sort. |
- * @param {number} sortOrder 1 or -1; determines whether to sort ascending or |
- * descending. |
- */ |
- function sortRows(rows, sortIndex, sortOrder) { |
- // Sort the table rows by sortIndex. |
- if (null == sortIndex || undefined == sortIndex) { |
- sortIndex = defaultSortIndex; |
- } |
- rows.sort(function(a, b) { |
- if (null == a) { return -sortOrder; } |
- if (null == b) { return sortOrder; } |
- if (null == a[sortIndex]) { return -sortOrder; } |
- if (null == b[sortIndex]) { return sortOrder; } |
- if (a[sortIndex] > b[sortIndex]) { return sortOrder; } |
- if (a[sortIndex] < b[sortIndex]) { return -sortOrder; } |
- return 0; |
- }); |
- } |
- |
- /** |
- * Rebuild the data table. |
- * |
- * @param {string} title The title of the table. |
- * @param {Array<string>} cols Array of column names. |
- * @param {Array<Array>} rows Array of row data. |
- * @param {string} containerId ID of the container where the table goes. |
- * @param {number} sortIndex The index of the column by which to sort. |
- * @param {boolean} reload Whether or not we reloaded data. This affects the |
- * sorting of rows. |
- */ |
- function rebuildTable(title, cols, rows, containerId, sortIndex, reload) { |
- var tableId = containerId + "_table"; |
- var oldTable = document.getElementById(tableId); |
- var lastSortIndex = null; |
- var sortOrder = -1; |
- if (oldTable) { |
- lastSortIndex = oldTable.sortIndex; |
- if (reload) { |
- sortIndex = lastSortIndex; |
- } else if (sortIndex == lastSortIndex) { |
- sortOrder = -oldTable.sortOrder; |
- } |
- } |
- sortRows(rows, sortIndex, sortOrder); |
- |
- var table = document.createElement("table"); |
- table.sortIndex = sortIndex; |
- table.sortOrder = sortOrder; |
- var thead = document.createElement("thead"); |
- |
- for (var i = 0; i < cols.length; ++i) { |
- var th = document.createElement("th"); |
- th.style.padding = "5px"; |
- th.style.textAlign = "right"; |
- var sortLink = document.createElement("a"); |
- sortLink.href = "#"; |
- sortLink.innerHTML = cols[i]; |
- sortLink.tableTitle = title; |
- sortLink.containerId = containerId; |
- sortLink.sortIndex = i; |
- sortLink.rowsObj = rows; |
- sortLink.colsObj = cols; |
- sortLink.addEventListener("click", function(event) { |
- var cols = event.target.colsObj; |
- var rows = event.target.rowsObj; |
- var containerId = event.target.containerId; |
- var sortIndex = event.target.sortIndex; |
- var tableTitle = event.target.tableTitle; |
- rebuildTable(tableTitle, cols, rows, containerId, sortIndex, false); |
- }); |
- th.appendChild(sortLink); |
- thead.appendChild(th); |
- } |
- |
- table.appendChild(thead); |
- |
- for (var i = 0; i < rows.length; ++i) { |
- var tr = document.createElement("tr"); |
- for (var j = 0; j < rows[i].length; ++j) { |
- var td = document.createElement("td"); |
- td.style.padding = "5px"; |
- td.style.textAlign = "right"; |
- var value = rows[i][j]; |
- if (typeof value == "number") { |
- value = parseFloat(value).toFixed(2); |
- } |
- // Add links for builder breakdowns to the first column. |
- if (tableId == "buildstep_div_table" && j == 0) { |
- var a = document.createElement("a"); |
- a.href = "#"; |
- a.buildStepName = value; |
- a.addEventListener("click", function(event) { |
- var name = event.target.buildStepName; |
- loadGraphiteData(null, null, null, null, name, |
- name + " on ...", "builder_div", |
- GROUP_NODE_BUILDER); |
- }); |
- a.innerHTML = value; |
- td.appendChild(a); |
- } else { |
- td.innerHTML = value; |
- } |
- tr.appendChild(td); |
- } |
- table.appendChild(tr); |
- } |
- var container = document.getElementById(containerId); |
- container.innerHTML = ""; |
- table.id = tableId; |
- var h2 = document.createElement("h2"); |
- h2.style.textAlign = "center"; |
- h2.innerHTML = title; |
- container.appendChild(h2); |
- container.appendChild(table); |
- } |
- |
- /** |
- * Callback function for receiving data from Graphite. Organizes the data and |
- * rebuilds the data table. |
- * |
- * @param {Array<Object>} data Data obtained from Graphite. |
- */ |
- function gotGraphiteData(data) { |
- var loadingData = loadingDone(); |
- makeTableFromData(data, loadingData["tableTitle"], |
- loadingData["containerId"]); |
- } |
- |
- /** |
- * Helper function for building Graphite data URLs. If any of the parameters |
- * are not provided, it obtains defaults from the text boxes on the page. |
- * |
- * @param {string} timeStart Unix timestamp; time period beginning. |
- * @param {string} timeEnd Unix timestamp; time period end. |
- * @param {string} masterFilter Filter the data by build master. |
- * @param {string} builderFilter Filter the data by builder. |
- * @param {string} buildStepFilter Filter the data by build step. |
- * @param {number} groupNode Column index for grouping Graphite data. |
- */ |
- function makeGraphiteURL(timeStart, timeEnd, masterFilter, builderFilter, |
- buildStepFilter, groupNode) { |
- if (!timeStart) { |
- timeStart = |
- dateTimeLocalToUnixUTC(document.getElementById("time_start").value); |
- } |
- if (!timeEnd) { |
- timeEnd = |
- dateTimeLocalToUnixUTC(document.getElementById("time_end").value); |
- } |
- if (!masterFilter) { |
- masterFilter = document.getElementById("master_filter").value; |
- } |
- if (!builderFilter) { |
- builderFilter = document.getElementById("builder_filter").value; |
- } |
- if (!buildStepFilter) { |
- buildStepFilter = document.getElementById("buildstep_filter").value; |
- } |
- var url = "http://skiamonitor.com/render?format=json&from=" + |
- timeStart + "&until=" + timeEnd; |
- var metricPrefixParts = ["buildbot", masterFilter, builderFilter, |
- buildStepFilter] |
- var metricPrefix = metricPrefixParts.join("."); |
- |
- for (var i = 0; i < metrics.length; ++i) { |
- var metric = metrics[i][0]; |
- var summaryMode = metrics[i][1]; |
- var fullMetricName = metricPrefix + "." + metric; |
- var groupByNode = "groupByNode(" + fullMetricName + "," + groupNode + |
- ",%22" + summaryMode + "%22)"; |
- var summarize = "summarize(" + groupByNode + ",%22" + SUMMARIZE_INTERVAL + |
- "%22,%22" + summaryMode + "%22,true)"; |
- var aliasByNode = "aliasByNode(" + summarize + ",0)"; |
- var aliasSub = "aliasSub(" + aliasByNode + ",%22$%22,%22." + metric + |
- "%22)"; |
- var fullTarget = "&target=" + aliasSub; |
- url += fullTarget; |
- } |
- return url; |
- } |
- |
- /** |
- * Load data from Graphite. |
- * |
- * @param {string} timeStart Unix timestamp; time period beginning. |
- * @param {string} timeEnd Unix timestamp; time period end. |
- * @param {string} masterFilter Filter the data by build master. |
- * @param {string} builderFilter Filter the data by builder. |
- * @param {string} buildStepFilter Filter the data by build step. |
- * @param {string} tableTitle Title for the table. |
- * @param {string} containerId ID of the element which will hold the table. |
- * @param {number} groupNode Column index for grouping Graphite data. |
- */ |
- function loadGraphiteData(timeStart, timeEnd, masterFilter, builderFilter, |
- buildStepFilter, tableTitle, containerId, groupNode) { |
- var url = makeGraphiteURL(timeStart, timeEnd, masterFilter, builderFilter, |
- buildStepFilter, groupNode); |
- var data = {"tableTitle": tableTitle, "containerId": containerId}; |
- loadingStart(url, data); |
- loadJSONP(url, "gotGraphiteData"); |
- } |
- |
- /** |
- * Function called when the "Load data" button is clicked. |
- */ |
- function reloadWithParams() { |
- var newQuery = |
- "from=" + dateTimeLocalToUnixUTC(document.getElementById("time_start").value) + |
- "&until=" + dateTimeLocalToUnixUTC(document.getElementById("time_end").value) + |
- "&master=" + document.getElementById("master_filter").value + |
- "&builder=" + document.getElementById("builder_filter").value + |
- "&buildstep=" + document.getElementById("buildstep_filter").value; |
- window.location.search = newQuery; |
- } |
- |
- /** |
- * Get the local timezone offset in seconds. |
- */ |
- function getTimezoneOffset() { |
- return new Date().getTimezoneOffset() * 60; |
- } |
- |
- /** |
- * Convert UTC to Local time. |
- */ |
- function UTCToLocal(unixDate) { |
- return unixDate - getTimezoneOffset(); |
- } |
- |
- /** |
- * Convert Local to UTC time. |
- */ |
- function localToUTC(unixDate) { |
- return unixDate + getTimezoneOffset(); |
- } |
- |
- /** |
- * Get the date in Unix format. |
- */ |
- function unixDate(date) { |
- var dateObj = null; |
- if (!date) { |
- dateObj = new Date() |
- } else { |
- dateObj = new Date(date); |
- } |
- return dateObj.getTime() / 1000; |
- } |
- |
- /** |
- * Convert from RFC 3339 to Unix format. |
- */ |
- function RFC3339ToUnix(date) { |
- return unixDate(date); |
- } |
- |
- /** |
- * Convert from Unix to RFC 3339 format, with no time zone information. |
- */ |
- function unixToRFC3339(timestamp) { |
- var date = new Date(parseInt(timestamp) * 1000); |
- function zfill(num) { |
- if (num < 10) { |
- return "0" + num; |
- } else { |
- return num; |
- } |
- } |
- return (date.getUTCFullYear() + "-" + |
- zfill(date.getUTCMonth() + 1) + "-" + |
- zfill(date.getUTCDate()) + "T" + |
- zfill(date.getUTCHours()) + ":" + |
- zfill(date.getUTCMinutes()) + ":" + |
- zfill(date.getUTCSeconds())); |
- } |
- |
- /** |
- * Convert from Unix timestamp to datetime-local RFC 3339 format. |
- */ |
- function unixUTCToDateTimeLocal(unix) { |
- return unixToRFC3339(UTCToLocal(unix)); |
- } |
- |
- /** |
- * Convert from datetime-local RFC 3339 format to Unix timestamp. |
- */ |
- function dateTimeLocalToUnixUTC(rfcLocal) { |
- return localToUTC(RFC3339ToUnix(rfcLocal)); |
- } |
- |
- /** |
- * Function called on page load. |
- */ |
- function init() { |
- // Load default query values. |
- var currentTime = unixDate(); |
- var timeEnd = currentTime; |
- var timeStart = currentTime - DEFAULT_TIME_PERIOD; |
- var masterFilter = DEFAULT_MASTER_FILTER; |
- var builderFilter = DEFAULT_BUILDER_FILTER; |
- var buildstepFilter = DEFAULT_BUILDSTEP_FILTER; |
- |
- // Retrieve any parameters from the URL. |
- var params = window.location.search.substring(1).split("&"); |
- // Remove empty string from parameters. |
- while (params.indexOf("") != -1) { |
- params.splice(params.indexOf("")); |
- } |
- for (var i = 0; i < params.length; ++i) { |
- var splitParam = params[i].split("="); |
- if (splitParam[0] == "from") { |
- timeStart = splitParam[1]; |
- } else if (splitParam[0] == "until") { |
- timeEnd = splitParam[1]; |
- } else if (splitParam[0] == "master") { |
- masterFilter = splitParam[1]; |
- } else if (splitParam[0] == "builder") { |
- builderFilter = splitParam[1]; |
- } else if (splitParam[0] == "buildstep") { |
- buildstepFilter = splitParam[1]; |
- } else { |
- console.error("Unknown parameter: " + splitParam[0]); |
- } |
- } |
- |
- // Fill the form with the appropriate values. |
- document.getElementById("time_start").value = |
- unixUTCToDateTimeLocal(timeStart); |
- document.getElementById("time_end").value = |
- unixUTCToDateTimeLocal(timeEnd); |
- document.getElementById("master_filter").value = masterFilter; |
- document.getElementById("builder_filter").value = builderFilter; |
- document.getElementById("buildstep_filter").value = buildstepFilter; |
- |
- // Decide what to do, based on the number of provided parameters. |
- if (params.length == 0) { |
- // Don't load data; we assume that the user might want to tweak the |
- // filters first. |
- } else if (params.length != 5) { |
- // Reload the page, filling in the rest of the parameters with their |
- // defaults. |
- reloadWithParams(); |
- } else { |
- // All parameters were provided. Load data. |
- loadGraphiteData(timeStart, timeEnd, masterFilter, builderFilter, |
- buildstepFilter, "Build Steps", "buildstep_div", |
- GROUP_NODE_BUILDSTEP); |
- } |
- } |
- |
- </script> |
- </head> |
- <body> |
- <div id="heading" style="font-size:2.5em; text-align:center; height:7%;"> |
- Skia Buildstep Dashboard |
- </div> |
- <div style="text-align:center;"> |
- Click a column header to sort that column. Click a buildstep to see its |
- results broken down into builders. |
- </div> |
- <div id="main_content_area" style="width:100%; height:90%; padding:0px; |
- margin:0px;"> |
- <div id="menu_div" |
- style="float:left; width:18%; height:100%; padding:0px; margin:0px;"> |
- <div style="width:100%;"> |
- <nobr>Time period:</nobr><br/> |
- From:<br/> |
- <input type="datetime-local" id="time_start" /><br/> |
- To:<br/> |
- <input type="datetime-local" id="time_end" /><br/> |
- <nobr>Build master filter:</nobr><br/> |
- <input type="text" id="master_filter" /><br/> |
- <nobr>Builder filter:</nobr><br/> |
- <input type="text" id="builder_filter" /><br/> |
- <nobr>Build step filter:</nobr><br/> |
- <input type="text" id="buildstep_filter" /><br/> |
- <input type="button" id="load_data_button" onClick="reloadWithParams();" value="Load Data"/> |
- </div> |
- <div id="logging_div" style="width:100%; padding:0px; margin:0px"></div> |
- </div> |
- <div id="buildstep_div" |
- style="float:left; width:41%; padding:0px; margin:0px"> |
- </div> |
- <div id="builder_div" |
- style="float:left; width:41%; padding:0px; margin:0px"> |
- </div> |
- </div> |
- <script type="text/javascript">init();</script> |
- </body> |
-</html> |
- |