diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-app-timeline.js hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-app-timeline.js index 831649ce060..aebc372323e 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-app-timeline.js +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-app-timeline.js @@ -17,15 +17,20 @@ */ import AbstractAdapter from './abstract'; +import Ember from 'ember'; export default AbstractAdapter.extend({ address: "timelineWebAddress", restNameSpace: "timelineV2", serverName: "ATS", - urlForFindRecord(id/*, modelName, snapshot*/) { + urlForFindRecord(id, modelName, snapshot) { var url = this._buildURL(); + var query = Ember.get(snapshot, 'adapterOptions.query'); url = url + '/apps/' + id + '?fields=ALL'; + if(query) { + url += '&' + Ember.$.param(query); + } return url; }, diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-timeline-container.js hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-timeline-container.js index f47335a3411..ad011754162 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-timeline-container.js +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/adapters/yarn-timeline-container.js @@ -26,10 +26,16 @@ export default AbstractAdapter.extend({ urlForQuery(query/*, modelName*/){ var url = this._buildURL(); - var app_attempt_id = query.app_attempt_id; + var app_id = query.app_id; + if(app_id) { + delete query.app_id; + } + else { + app_id = Converter.attemptIdToAppId(query.app_attempt_id); + delete query.app_attempt_id; + } query.fields = 'ALL'; - delete query.app_attempt_id; - url = url + '/apps/' + Converter.attemptIdToAppId(app_attempt_id) + + url = url + '/apps/' + app_id + '/entities/YARN_CONTAINER'; return url; }, diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/resource-time-line-chart.js hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/resource-time-line-chart.js new file mode 100644 index 00000000000..e647f3e44ae --- /dev/null +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/components/resource-time-line-chart.js @@ -0,0 +1,429 @@ +/** + * 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. + */ + +import Ember from 'ember'; +import BaseChartComponent from 'yarn-ui/components/base-chart-component'; +import Converter from 'yarn-ui/utils/converter'; + +export default BaseChartComponent.extend({ + optionsData: {}, + dataGlobal: [], + containerDataGlobal: [], + renderLineChart: function() { + var g = this.chart.g; + var layout = this.getLayout(); + var colors = d3.scale.category10().domain(['memory', 'cpu']); + var data = this.dataGlobal; + var containerData = this.containerDataGlobal; + var margins = { + top: 20, + right: 250, + bottom: 50, + left: 100 + }; + var self = this; + self.chart.svg.attr("width", layout.x2 + margins.right); + var parent = d3.select("#" + this.get("parentId")); + var bbox = parent.node().getBoundingClientRect(); + this.chart.w = bbox.width - margins.right + 30; + layout.x2 = this.chart.w; + this.renderTitleAndBG(g, this.get("title"), layout); + + var startTime = $('#datetimepicker6').data("DateTimePicker").date(); + var endTime = $('#datetimepicker7').data("DateTimePicker").date(); + + var xScale = d3.time.scale() + .range([margins.left, layout.x2]) + .domain([moment(startTime), moment(endTime)]); + + var yScale = d3.scale.linear() + .range([layout.y2 - margins.bottom, margins.bottom + 20]) + .domain([0, 100]); + + var xAxis = d3.svg.axis() + .scale(xScale); + + var yAxis = d3.svg.axis() + .scale(yScale) + .orient("left"); + + g.append("clipPath") + .attr("id", "clip") + .append("rect") + .attr("x", margins.left - 2) + .attr("y", layout.y1) + .attr("width", layout.x2 - layout.x1 - margins.left + 10) + .attr("height", layout.y2 - layout.y1); + + g.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + (layout.y2 - margins.bottom) + ")") + .text("Time") + .call(xAxis) + .selectAll("text") + .style("text-anchor", "end") + .attr("dx", "-.8em") + .attr("dy", ".15em") + .attr("transform", "rotate(-55)" ); + + g.append("g") + .attr("transform", "translate(" + (margins.left) + ",0)") + .attr("class", "y axis") + .call(yAxis) + .append("text") + .attr("transform", "rotate(-90)") + .attr("y", -margins.left / 2) + .attr("x", -(layout.y2 - 2 * margins.bottom - 20) / 2) + .attr("text-anchor", "end") + .text("Percentage of Cluster (%)"); + + var lineGen = d3.svg.line() + .x(function(d) { + return xScale(d[0]); + }) + .y(function(d) { + return yScale(d[1]); + }); + + var lineGen2 = d3.svg.line() + .x(function(d) { + return xScale(d[0]); + }) + .y(function(d) { + return yScale(d[1]); + }) + .interpolate('step-after'); + + var lineArea = d3.svg.area() + .x(function(d) { + return xScale(d[0]); + }) + .y0(layout.y2 - margins.bottom) + .y1(function(d) { + return yScale(d[1]); + }) + .interpolate('step-after'); + + data.forEach(function(d, i){ + g.append('path') + .attr('d', lineArea(containerData[i].values)) + .attr("stroke-width", 0) + .attr("clip-path", "url(#clip)") + .attr("fill", function(){ + return d3.hsl(colors(d.id.toLowerCase())); + }) + .attr("opacity", 0) + .attr("id", d.id + '-container-area'); + + g.append('path') + .attr('d', lineGen2(containerData[i].values)) + .attr("fill", 'none') + .attr("opacity", 1.0) + .attr("stroke-width", 5) + .attr('stroke', function(){ + return d3.hsl(colors(d.id.toLowerCase())).brighter(1.0); + }) + .attr("clip-path", "url(#clip)") + .attr('id', d.id + '-container-line') + .on("mouseover", function() { + self.tooltip + .style("left", (d3.event.pageX) + "px") + .style("top", (d3.event.pageY - 28) + "px"); + d3.select("#" + d.id + "-container-area") + .attr("opacity", 0.1); + }) + .on("mousemove", function() { + self.tooltip.style("opacity", 0.9); + self.tooltip.html("Allocated " + d.id.toLowerCase()) + .style("left", (d3.event.pageX) + "px") + .style("top", (d3.event.pageY - 28) + "px"); + }) + .on("mouseout", function() { + self.tooltip.style("opacity", 0); + d3.select("#" + d.id + "-container-area") + .attr("opacity", 0); + }); + }); + + data.forEach(function(d, i){ + g.append('path') + .attr('d', lineGen(d.values)) + .attr('stroke', function(){ + return d3.hsl(colors(d.id.toLowerCase())); + }) + .attr("clip-path", "url(#clip)") + .attr('stroke-width', 2) + .attr('id', d.id + '-line') + .attr('fill', 'none') + .on("mouseover", function() { + self.tooltip + .style("left", (d3.event.pageX) + "px") + .style("top", (d3.event.pageY - 28) + "px"); + }) + .on("mousemove", function() { + self.tooltip.style("opacity", 0.9); + self.tooltip.html("Utilized " + d.id.toLowerCase()) + .style("left", (d3.event.pageX) + "px") + .style("top", (d3.event.pageY - 28) + "px"); + }) + .on("mouseout", function() { + self.tooltip.style("opacity", 0); + }); + g.selectAll("points") + .data(d.values) + .enter() + .append("circle") + .attr("clip-path", "url(#clip)") + .attr("r", 3.5) + .attr("cx", function(d) { + return xScale(d[0]); + }) + .attr("cy", function(d) { + return yScale(d[1]); + }) + .attr('id', d.id + '-line') + .on("mouseover", function() { + self.tooltip + .style("left", (d3.event.pageX) + "px") + .style("top", (d3.event.pageY - 28) + "px"); + var circle = d3.select(this); + circle.transition().duration(500) + .attr("r", 15) + .attr("opacity", 0.5); + }) + .on("mousemove", function(d) { + var data = d; + if (d.data) { + data = d.data; + } + + self.tooltip.style("opacity", 0.9); + var value = data[1]; + self.tooltip.html(data[0] + " : " + value) + .style("left", (d3.event.pageX) + "px") + .style("top", (d3.event.pageY - 28) + "px"); + }) + .on("mouseout", function() { + self.tooltip.style("opacity", 0); + var circle = d3.select(this); + circle.transition().duration(500) + .attr("r", 3.5 ) + .attr("opacity", 1.0); + }); + + var xDisp = 30; + var yDisp = 140; + g.append("rect") + .attr("x", layout.x2 + xDisp + 80) + .attr("y", 20 * i + yDisp) + .attr("width", 10) + .attr("height", 10) + .attr("fill", function(){ + var lineId = d.id + "-line"; + var active = self.optionsData[lineId] ? false : true; + var display = !active ? 'none' : 'inline'; + d3.selectAll("#" + lineId).style("display", display); + var fill = !active ? 'white' : d3.hsl(colors(d.id.toLowerCase())); + return fill; + }) + .attr('stroke-width', 2) + .attr('stroke', function(){ + return "black"; + }) + .attr('id', d.id + '-checkbox') + .on("click", function(){ + var lineId = d.id + "-line"; + var active = self.optionsData[lineId] ? false : true; + var display = active ? 'none' : 'inline'; + var fill = active ? 'white' : d3.hsl(colors(d.id.toLowerCase())); + d3.selectAll("#" + lineId).style("display", display); + d3.select(this).attr("fill", fill); + self.optionsData[lineId] = active; + }); + + g.append("rect") + .attr("x", layout.x2 + xDisp + 100) + .attr("y", 20 * i + yDisp) + .attr("width", 10) + .attr("height", 10) + .attr("fill", function(){ + var containerLineId = d.id + "-container-line"; + var active = self.optionsData[containerLineId] ? false : true; + var display = !active ? 'none' : 'inline'; + var fill = !active ? 'white' : d3.hsl(colors(d.id.toLowerCase())).brighter(1.0); + d3.selectAll('#' + containerLineId).style("display", display); + d3.selectAll('#' + d.id + "-container-area").style("display", display); + return fill; + }) + .attr('stroke-width', 2) + .attr('stroke', function(){ + return "black"; + }) + .attr('id', d.id + '-checkbox') + .on("click", function(){ + var containerLineId = d.id + "-container-line"; + var active = self.optionsData[containerLineId] ? false : true; + var display = active ? 'none' : 'inline'; + var fill = active ? 'white' : d3.hsl(colors(d.id.toLowerCase())).brighter(1.0); + d3.selectAll('#' + containerLineId).style("display", display); + d3.selectAll('#' + d.id + "-container-area").style("display", display); + d3.select(this).attr("fill", fill); + self.optionsData[containerLineId] = active; + }); + + g.append("text") + .attr("x", layout.x2 + xDisp) + .attr("y", 20 * i + yDisp + 10) + .text(d.id); + }); + var xPos = layout.x2 + 115; + var yPos = 130; + g.append("text") + .attr("transform", "translate("+xPos+","+ yPos +")rotate(-60)") + .text("Utilized"); + + xPos += 25; + g.append("text") + .attr("transform", "translate("+xPos+","+ yPos +")rotate(-60)") + .text("Allocated"); + }, + + draw: function() { + this.renderLineChart(); + }, + + _dataChange: Ember.observer("data", function() { + this.chart.g.selectAll("*").remove(); + d3.select("#options").selectAll("*").remove(); + this.initData(); + this.draw(); + }), + + didInsertElement: function() { + var containers = this.get("containers"); + var self = this; + var startTime = self.get("startTime"); + var endTime = self.get("endTime"); + containers.forEach(function(d) { + if(d.get("finishedTime") > endTime) { + endTime = d.get("finishedTime"); + } + }); + $('#datetimepicker6').datetimepicker({ + format: "MM/DD/YYYY hh:mm:ss", + useCurrent: false, + defaultDate: startTime, + minDate: startTime, + maxDate: endTime + }); + $('#datetimepicker7').datetimepicker({ + format: "MM/DD/YYYY hh:mm:ss", + useCurrent: false, + defaultDate: endTime, + minDate: startTime, + maxDate: endTime + }); + self.set("endTime", endTime); + this.initChart(); + this.initData(); + this.draw(); + $('#datetimepicker6').datetimepicker().on('dp.change', function(e) { + $('#datetimepicker7').data("DateTimePicker").minDate(e.date); + self.chart.g.selectAll("*").remove(); + d3.select("#options").selectAll("*").remove(); + self.draw(); + }); + $('#datetimepicker7').datetimepicker().on('dp.change', function(e) { + $('#datetimepicker6').data("DateTimePicker").maxDate(e.date); + self.chart.g.selectAll("*").remove(); + d3.select("#options").selectAll("*").remove(); + self.draw(); + }); + }, + + initData: function() { + var self = this; + var data = this.get("data"); + var dataCopy = $.extend(true, [], data); + var containers = this.get("containers"); + var startTime = this.get("startTime"); + var endTime = this.get("endTime"); + var parseDate = d3.time.format("%Y/%m/%d %H:%M:%S").parse; + var containersList = []; + containers.forEach(function(d) { + var toInsert = {}; + toInsert.startDate = d.get("startedTime"); + toInsert.endDate = d.get("finishedTime"); + toInsert.memory = d.get("allocatedMB") / self.get("totalMB") * 100; + toInsert.cpu = d.get("allocatedVCores") / self.get("totalCPU") * 100; + containersList.push(toInsert); + }); + var allDates = []; + for(var i = 0; i < containersList.length; i++) { + if(allDates.indexOf(containersList[i].startDate) === -1) { + allDates.push(containersList[i].startDate); + } + if(allDates.indexOf(containersList[i].endDate) === -1) { + allDates.push(containersList[i].endDate); + } + } + allDates = allDates.sort(); + var memData = []; + var cpuData = []; + memData.push([parseDate(startTime), 0]); + cpuData.push([parseDate(startTime), 0]); + for(i = 0; i < allDates.length - 1; i++) { + var first = allDates[i]; + var second = allDates[i + 1]; + var valMem = 0; + var valCpu = 0; + for(var j = 0; j < containersList.length; j++) { + var curr = containersList[j]; + if(curr.startDate <= first && curr.endDate >= second) { + valMem += curr.memory; + valCpu += curr.cpu; + } + } + memData.push([parseDate(first), valMem]); + cpuData.push([parseDate(first), valCpu]); + } + memData.push([parseDate(endTime), 0]); + cpuData.push([parseDate(endTime), 0]); + var containerData = [{'id': 'MEMORY', 'values': memData}, {'id': 'CPU', 'values': cpuData}]; + data.forEach(function(d, i){ + var values = []; + values.push([parseDate(endTime), 0]); + var dataKeys = Object.keys(d.values); + for(var j = 0; j < dataKeys.length; j++) { + var key = dataKeys[j]; + let currVal = d.values[key]; + if(d.id === "MEMORY") { + currVal = Converter.memoryBytesToMBOnly(currVal) / self.get("totalMB") * 100; + } + else if(d.id === "CPU") { + currVal = currVal / self.get("totalCPU"); + } + values.push([parseDate(Converter.timeStampToDate(key)), currVal]); + } + values.push([parseDate(startTime), 0]); + dataCopy[i].values = values; + }); + self.dataGlobal = dataCopy; + self.containerDataGlobal = containerData; + } +}); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-app-timeline.js hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-app-timeline.js index fa5223f5256..30e19b5ba38 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-app-timeline.js +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/models/yarn-app-timeline.js @@ -50,6 +50,7 @@ export default DS.Model.extend({ clusterUsagePercentage: DS.attr('number'), queueUsagePercentage: DS.attr('number'), currentAppAttemptId: DS.attr('string'), + metrics: DS.attr(), isFailed: function() { return this.get('finalStatus') === "FAILED"; diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-app/charts.js hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-app/charts.js index 1b687db4025..7b742214513 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-app/charts.js +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/routes/yarn-app/charts.js @@ -40,7 +40,19 @@ export default AbstractRoute.extend({ }.bind(this)); }.bind(this)), - nodes: this.store.findAll('yarn-rm-node') + nodes: this.store.findAll('yarn-rm-node'), + clusterMetrics: this.store.findAll('cluster-metric'), + timelineApp: this.store.findRecord('yarn-app-timeline', param.app_id, { + adapterOptions: {query: + {metricstoretrieve: "MEMORY,CPU", metricslimit: 2147483647}} + }).catch(function() { + return []; + }), + timelineContainers: this.store.query('yarn-timeline-container', { + app_id: param.app_id + }).catch(function() { + return []; + }) }); }, @@ -49,5 +61,8 @@ export default AbstractRoute.extend({ this.store.unloadAll('yarn-app-attempt'); this.store.unloadAll('yarn-container'); this.store.unloadAll('yarn-rm-node'); + this.store.unloadAll('cluster-metric'); + this.store.unloadAll('yarn-app-timeline'); + this.store.unloadAll('yarn-timeline-container'); } }); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-app-timeline.js hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-app-timeline.js index 680fe8ccdfe..92c0134ab43 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-app-timeline.js +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/serializers/yarn-app-timeline.js @@ -62,10 +62,10 @@ export default DS.JSONAPISerializer.extend({ numAMContainerPreempted: 0, clusterUsagePercentage: 0, queueUsagePercentage: 0, - currentAppAttemptId: payload.info.YARN_APPLICATION_LATEST_APP_ATTEMPT + currentAppAttemptId: payload.info.YARN_APPLICATION_LATEST_APP_ATTEMPT, + metrics: payload.metrics } }; - return fixedPayload; }, diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css index 8b8ea566258..6b0632c7900 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/styles/app.css @@ -397,3 +397,34 @@ div.attempt-info-panel table > tbody > tr > td:last-of-type { width: 14px; display: inline-block; } + +.axis path { + fill: none; + stroke: black; +} + +.axis text { + font-size: 14px; +} + +#datetimepicker6 { + margin-right: 10px; +} + +#datetimepicker6 td, #datetimepicker7 td { + padding: 5px !important; +} + +#dates { + padding: 0px; +} + +.datetime { + padding-top: 10px; +} + +#dates .form-group { + width: 220px; + margin-left: 0px; + padding: 0px; +} diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app/charts.hbs hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app/charts.hbs index 8d3388ab7d6..d5afdbd0765 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app/charts.hbs +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/templates/yarn-app/charts.hbs @@ -34,6 +34,49 @@ parentId="stackd-bar-chart-ncontainer" title=(concat 'Running #Containers by nodes for: [' model.appId ']')}} + {{else if model.timelineApp.metrics}} +
+
+
+ Resource Utilization vs Allocation Over Time +
+
+
+ {{resource-time-line-chart + title="" + parentId="resources-line-chart" + data=model.timelineApp.metrics + totalMB=model.clusterMetrics.firstObject.totalMB + totalCPU=model.clusterMetrics.firstObject.totalVirtualCores + containers=model.timelineContainers + ratio=0.8 + startTime=model.timelineApp.startTime + endTime=model.timelineApp.finishedTime + }} +
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ + + + +
+
+
+
{{else}}

No resource usage data is available for this application!

diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/converter.js hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/converter.js index 7c9a1f81781..41773761cfa 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/converter.js +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/app/utils/converter.js @@ -165,5 +165,9 @@ export default { unit = "GB"; } return value.toFixed(1) + " " + unit; + }, + memoryBytesToMBOnly: function(mem) { + var value = mem / (1024 * 1024); + return value.toFixed(1); } }; diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower-shrinkwrap.json hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower-shrinkwrap.json index b0f3aa360b0..55125361c1e 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower-shrinkwrap.json +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower-shrinkwrap.json @@ -2,6 +2,9 @@ "https://github.com/DataTables/DataTables.git": { "1.10.15": "1.10.15" }, + "https://github.com/Eonasdan/bootstrap-datetimepicker.git": { + "4.17.47": "25c11d79e614bc6463a87c3dd9cbf8280422e006" + }, "https://github.com/components/ember-data.git": { "2.1.0": "d8b4d3092f67afe22d9d374c40d719d557915fa3" }, diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower.json hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower.json index e1ab943fc18..8d48812b540 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower.json +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/bower.json @@ -21,6 +21,7 @@ "spin.js": "~2.3.2", "momentjs": "~2.10.6", "select2": "4.0.0", - "snippet-ss": "~1.11.0" + "snippet-ss": "~1.11.0", + "eonasdan-bootstrap-datetimepicker": "^4.17.47" } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/ember-cli-build.js hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/ember-cli-build.js index 4799f9286f0..fcb71e00237 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/ember-cli-build.js +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui/src/main/webapp/ember-cli-build.js @@ -49,7 +49,8 @@ module.exports = function(defaults) { app.import('bower_components/bootstrap/dist/css/bootstrap.css'); app.import('bower_components/bootstrap/dist/css/bootstrap-theme.css'); app.import('bower_components/bootstrap/dist/js/bootstrap.min.js'); - + app.import('bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css'); + app.import('bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js'); // Use `app.import` to add additional libraries to the generated // output files. //