YARN-6402. Move 'Long Running Services' to an independent tab at top level for new Yarn UI. Contributed by Akhil PB.

This commit is contained in:
Sunil G 2017-04-20 12:33:53 +05:30
parent 74a723852d
commit c0cf11e107
26 changed files with 594 additions and 128 deletions

View File

@ -23,7 +23,7 @@ export default AbstractAdapter.extend({
restNameSpace: "cluster", restNameSpace: "cluster",
serverName: "RM", serverName: "RM",
urlForQuery(query/*, modelName*/) { urlForQuery(/*query, modelName*/) {
var url = this._buildURL(); var url = this._buildURL();
url = url + '/apps'; url = url + '/apps';
return url; return url;

View File

@ -0,0 +1,23 @@
/**
* 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';
export default Ember.Component.extend({
content: null
});

View File

@ -34,6 +34,7 @@ export default Ember.Component.extend({
_selected: undefined, _selected: undefined,
gridColumns: [], gridColumns: [],
gridRows: [], gridRows: [],
serviceName: undefined,
selected: function() { selected: function() {
return this._selected; return this._selected;
@ -290,6 +291,7 @@ export default Ember.Component.extend({
setAttemptsGridColumnsAndRows: function() { setAttemptsGridColumnsAndRows: function() {
var self = this; var self = this;
var columns = []; var columns = [];
var serviceName = this.get('serviceName');
columns.push({ columns.push({
id: 'id', id: 'id',
@ -298,10 +300,11 @@ export default Ember.Component.extend({
cellComponentName: 'em-table-linked-cell', cellComponentName: 'em-table-linked-cell',
minWidth: '300px', minWidth: '300px',
getCellContent: function(row) { getCellContent: function(row) {
var attemptId = row.get('id');
var query = serviceName? '?service='+serviceName : '';
return { return {
displayText: row.get('id'), displayText: attemptId,
routeName: 'yarn-app-attempt', href: `#/yarn-app-attempt/${attemptId}${query}`
id: row.get('id')
}; };
} }
}, { }, {
@ -326,7 +329,7 @@ export default Ember.Component.extend({
id: 'appMasterContainerId', id: 'appMasterContainerId',
headerTitle: 'AM Container ID', headerTitle: 'AM Container ID',
contentPath: 'appMasterContainerId', contentPath: 'appMasterContainerId',
minWidth: '300px' minWidth: '350px'
}, { }, {
id: 'amNodeId', id: 'amNodeId',
headerTitle: 'AM Node ID', headerTitle: 'AM Node ID',
@ -384,7 +387,7 @@ export default Ember.Component.extend({
id: 'id', id: 'id',
headerTitle: 'Container ID', headerTitle: 'Container ID',
contentPath: 'id', contentPath: 'id',
minWidth: '300px' minWidth: '350px'
}, { }, {
id: 'startedTime', id: 'startedTime',
headerTitle: 'Started Time', headerTitle: 'Started Time',

View File

@ -19,80 +19,145 @@
import Ember from 'ember'; import Ember from 'ember';
import ColumnDef from 'em-table/utils/column-definition'; import ColumnDef from 'em-table/utils/column-definition';
import TableDef from 'em-table/utils/table-definition';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
columns: function() { tableDefinition: TableDef.create({
var colums = []; sortColumnId: 'stTime',
colums.push({ sortOrder: 'desc'
id: 'appId', }),
headerTitle: 'Application ID',
contentPath: 'id', columns: function() {
cellComponentName: 'em-table-linked-cell', var colums = [];
getCellContent: function(row) { colums.push({
return { id: 'appId',
displayText: row.id, headerTitle: 'Application ID',
routeName: 'yarn-app', contentPath: 'id',
id: row.id cellComponentName: 'em-table-linked-cell',
}; minWidth: "250px",
}, getCellContent: function(row) {
minWidth: "250px" return {
}, { displayText: row.id,
id: 'appType', href: `#/yarn-app/${row.id}`
headerTitle: 'Application Type', };
contentPath: 'applicationType', }
}, { }, {
id: 'appName', id: 'appType',
headerTitle: 'Application Name', headerTitle: 'Application Type',
contentPath: 'appName', contentPath: 'applicationType',
}, { }, {
id: 'appUsr', id: 'appName',
headerTitle: 'User', headerTitle: 'Application Name',
contentPath: 'user', contentPath: 'appName',
minWidth: "50px" }, {
}, { id: 'appUsr',
id: 'queue', headerTitle: 'User',
headerTitle: 'Queue', contentPath: 'user',
contentPath: 'queue', minWidth: "50px"
}, { }, {
id: 'state', id: 'queue',
headerTitle: 'State', headerTitle: 'Queue',
contentPath: 'state', contentPath: 'queue',
cellComponentName: 'em-table-status-cell', }, {
minWidth: "50px" id: 'state',
}, { headerTitle: 'State',
id: 'progress', contentPath: 'state',
headerTitle: 'Progress', cellComponentName: 'em-table-status-cell',
contentPath: 'progress', minWidth: "50px"
cellComponentName: 'em-table-progress-cell', }, {
cellDefinition: { id: 'progress',
valueMax: 100 headerTitle: 'Progress',
}, contentPath: 'progress',
}, { cellComponentName: 'em-table-progress-cell',
id: 'stTime', cellDefinition: {
headerTitle: 'Start Time', valueMax: 100
contentPath: 'startTime', }
}, { }, {
id: 'elTime', id: 'stTime',
headerTitle: 'Elapsed Time', headerTitle: 'Start Time',
contentPath: 'elapsedTime', contentPath: 'startTime',
cellDefinition: { }, {
type: "duration" id: 'elTime',
} headerTitle: 'Elapsed Time',
}, { contentPath: 'elapsedTime',
id: 'finishTime', cellDefinition: {
headerTitle: 'Finished Time', type: "duration"
contentPath: 'validatedFinishedTs', }
observePath: true }, {
}, { id: 'finishTime',
id: 'priority', headerTitle: 'Finished Time',
headerTitle: 'Priority', contentPath: 'validatedFinishedTs',
contentPath: 'priority', observePath: true
}, { }, {
id: 'cluster', id: 'priority',
headerTitle: '%Cluster', headerTitle: 'Priority',
contentPath: 'clusterUsagePercentage', contentPath: 'priority',
observePath: true }, {
}); id: 'cluster',
return ColumnDef.make(colums); headerTitle: '%Cluster',
}.property() contentPath: 'clusterUsagePercentage',
observePath: true
});
return ColumnDef.make(colums);
}.property(),
serviceColumns: function() {
var colums = [];
colums.push({
id: 'appName',
headerTitle: 'Service Name',
contentPath: 'appName',
minWidth: "200px",
cellComponentName: 'em-table-linked-cell',
getCellContent: function(row) {
return {
displayText: row.get('appName'),
href: `#/yarn-app/${row.id}?service=${row.get('appName')}`
};
}
}, {
id: 'appId',
headerTitle: 'Application ID',
contentPath: 'id',
minWidth: "250px"
}, {
id: 'state',
headerTitle: 'State',
contentPath: 'state',
cellComponentName: 'em-table-status-cell',
minWidth: "50px"
}, {
id: 'cluster',
headerTitle: '%Cluster',
contentPath: 'clusterUsagePercentage',
observePath: true
}, {
id: 'elTime',
headerTitle: 'Elapsed Time',
contentPath: 'elapsedTime',
cellDefinition: {
type: "duration"
},
minWidth: "200px"
}, {
id: 'appUsr',
headerTitle: 'User',
contentPath: 'user',
minWidth: "50px"
}, {
id: 'queue',
headerTitle: 'Queue',
contentPath: 'queue',
}, {
id: 'stTime',
headerTitle: 'Started Time',
contentPath: 'startTime',
}, {
id: 'finishTime',
headerTitle: 'Finished Time',
contentPath: 'validatedFinishedTs',
observePath: true
});
return ColumnDef.make(colums);
}.property(),
}); });

View File

@ -19,10 +19,14 @@
import Ember from 'ember'; import Ember from 'ember';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
queryParams: ["service"],
service: undefined,
breadcrumbs: Ember.computed("model.attempt.appId", function () { breadcrumbs: Ember.computed("model.attempt.appId", function () {
var appId = this.get("model.attempt.appId"); var appId = this.get("model.attempt.appId");
return [{ var attemptId = this.get("model.attempt.id");
var serviceName = this.get('service');
var breadcrumbs = [{
text: "Home", text: "Home",
routeName: 'application' routeName: 'application'
},{ },{
@ -30,11 +34,31 @@ export default Ember.Controller.extend({
routeName: 'yarn-apps.apps' routeName: 'yarn-apps.apps'
}, { }, {
text: `App [${appId}]`, text: `App [${appId}]`,
routeName: 'yarn-app', href: `#/yarn-app/${appId}`
model: appId
}, { }, {
text: "Attempt", text: "Attempts",
href: `#/yarn-app-attempts/${appId}`
}, {
text: `Attempt [${attemptId}]`
}]; }];
if (serviceName) {
breadcrumbs = [{
text: "Home",
routeName: 'application'
}, {
text: "Services",
routeName: 'yarn-services'
}, {
text: `${serviceName} [${appId}]`,
href: `#/yarn-app/${appId}?service=${serviceName}`
}, {
text: "Attempts",
href: `#/yarn-app-attempts/${appId}?service=${serviceName}`
}, {
text: `Attempt [${attemptId}]`
}];
}
return breadcrumbs;
}) })
}); });

View File

@ -19,10 +19,13 @@
import Ember from 'ember'; import Ember from 'ember';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
queryParams: ["service"],
service: undefined,
breadcrumbs: Ember.computed("model.appId", function () { breadcrumbs: Ember.computed("model.appId", function () {
var appId = this.get("model.appId"); var appId = this.get("model.appId");
return [{ var serviceName = this.get('service');
var breadcrumbs = [{
text: "Home", text: "Home",
routeName: 'application' routeName: 'application'
},{ },{
@ -30,11 +33,25 @@ export default Ember.Controller.extend({
routeName: 'yarn-apps.apps' routeName: 'yarn-apps.apps'
}, { }, {
text: `App [${appId}]`, text: `App [${appId}]`,
routeName: 'yarn-app', href: `#/yarn-app/${appId}`
model: appId
}, { }, {
text: "Attempts", text: "Attempts",
}]; }];
if (serviceName) {
breadcrumbs = [{
text: "Home",
routeName: 'application'
}, {
text: "Services",
routeName: 'yarn-services'
}, {
text: `${serviceName} [${appId}]`,
href: `#/yarn-app/${appId}?service=${serviceName}`
}, {
text: "Attempts"
}];
}
return breadcrumbs;
}) })
}); });

View File

@ -19,10 +19,13 @@
import Ember from 'ember'; import Ember from 'ember';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
queryParams: ["service"],
service: undefined,
breadcrumbs: Ember.computed("model.app.id", function () { breadcrumbs: Ember.computed("model.app.id", function () {
var appId = this.get("model.app.id"); var appId = this.get("model.app.id");
return [{ var serviceName = this.get('service');
var breadcrumbs = [{
text: "Home", text: "Home",
routeName: 'application' routeName: 'application'
},{ },{
@ -30,9 +33,21 @@ export default Ember.Controller.extend({
routeName: 'yarn-apps.apps' routeName: 'yarn-apps.apps'
}, { }, {
text: `App [${appId}]`, text: `App [${appId}]`,
routeName: 'yarn-app', href: `#/yarn-app/${appId}`
model: appId
}]; }];
if (serviceName) {
breadcrumbs = [{
text: "Home",
routeName: 'application'
}, {
text: "Services",
routeName: 'yarn-services'
}, {
text: `${serviceName} [${appId}]`,
href: `#/yarn-app/${appId}?service=${serviceName}`
}];
}
return breadcrumbs;
}), }),
amHostHttpAddressFormatted: Ember.computed('model.app.amHostHttpAddress', function() { amHostHttpAddressFormatted: Ember.computed('model.app.amHostHttpAddress', function() {

View File

@ -16,9 +16,6 @@
* limitations under the License. * limitations under the License.
*/ */
import Ember from 'ember';
import ColumnDef from 'em-table/utils/column-definition';
import AppTableController from '../app-table-columns'; import AppTableController from '../app-table-columns';
export default AppTableController.extend({ export default AppTableController.extend({

View File

@ -16,9 +16,6 @@
* limitations under the License. * limitations under the License.
*/ */
import Ember from 'ember';
import ColumnDef from 'em-table/utils/column-definition';
import AppTableController from '../app-table-columns'; import AppTableController from '../app-table-columns';
export default AppTableController.extend({ export default AppTableController.extend({

View File

@ -17,18 +17,85 @@
*/ */
import Ember from 'ember'; import Ember from 'ember';
import AppTableController from './app-table-columns';
export default Ember.Controller.extend({
export default AppTableController.extend({
breadcrumbs: [{ breadcrumbs: [{
text: "Home", text: "Home",
routeName: 'application' routeName: 'application'
}, { }, {
text: "Applications", text: "Services",
routeName: 'yarn-apps.apps',
}, {
text: "Long Running Services",
routeName: 'yarn-services', routeName: 'yarn-services',
}] }],
getFinishedServicesDataForDonutChart: Ember.computed('model.apps', function() {
var finishdApps = 0;
var failedApps = 0;
var killedApps = 0;
this.get('model.apps').forEach(function(service){
if (service.get('state') === "FINISHED") {
finishdApps++;
}
if (service.get('state') === "FAILED") {
failedApps++;
}
if (service.get('state') === "KILLED") {
killedApps++;
}
});
var arr = [];
arr.push({
label: "Completed",
value: finishdApps
});
arr.push({
label: "Killed",
value: killedApps
});
arr.push({
label: "Failed",
value: failedApps
});
return arr;
}),
getRunningServicesDataForDonutChart: Ember.computed('model.apps', function() {
var pendingApps = 0;
var runningApps = 0;
this.get('model.apps').forEach(function(service){
if (service.get('state') === "RUNNING") {
runningApps++;
}
if (service.get('state') === "ACCEPTED" ||
service.get('state') === "SUBMITTED" ||
service.get('state') === "NEW" ||
service.get('state') === "NEW_SAVING") {
pendingApps++;
}
});
var arr = [];
arr.push({
label: "Pending",
value: pendingApps
});
arr.push({
label: "Running",
value: runningApps
});
return arr;
}),
}); });

View File

@ -59,11 +59,15 @@ export default DS.Model.extend({
validatedFinishedTs: function() { validatedFinishedTs: function() {
if (this.get("finishedTime") < this.get("startTime")) { if (this.get("finishedTime") < this.get("startTime")) {
return ""; return "N/A";
} }
return this.get("finishedTime"); return this.get("finishedTime");
}.property("finishedTime"), }.property("finishedTime"),
formattedElapsedTime: function() {
return Converter.msToElapsedTimeUnit(this.get('elapsedTime'));
}.property('elapsedTime'),
allocatedResource: function() { allocatedResource: function() {
return Converter.resourceToString(this.get("allocatedMB"), this.get("allocatedVCores")); return Converter.resourceToString(this.get("allocatedMB"), this.get("allocatedVCores"));
}.property("allocatedMB", "allocatedVCores"), }.property("allocatedMB", "allocatedVCores"),

View File

@ -28,6 +28,7 @@ Router.map(function() {
this.route('apps'); this.route('apps');
this.route('services'); this.route('services');
}); });
this.route('yarn-services');
this.route('yarn-nodes', function(){ this.route('yarn-nodes', function(){
this.route('table'); this.route('table');
this.route('heatmap'); this.route('heatmap');

View File

@ -0,0 +1,34 @@
/**
* 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 AbstractRoute from './abstract';
export default AbstractRoute.extend({
model() {
return Ember.RSVP.hash({
apps: this.store.query('yarn-app', {
applicationTypes: "org-apache-slider"
}),
});
},
unloadAll() {
this.store.unloadAll('yarn-app');
}
});

View File

@ -305,3 +305,12 @@ li a.navigation-link.ember-view {
.donut-chart svg { .donut-chart svg {
width: 100%; width: 100%;
} }
div.attempt-info-panel table {
table-layout: fixed;
}
div.attempt-info-panel table > tbody > tr > td:last-of-type {
overflow: hidden;
text-overflow: ellipsis;
}

View File

@ -46,13 +46,18 @@
<span class="sr-only">(current)</span> <span class="sr-only">(current)</span>
{{/link-to}} {{/link-to}}
{{/link-to}} {{/link-to}}
{{#link-to 'yarn-apps.apps' tagName="li" currentWhen="yarn-apps.apps yarn-apps.services"}} {{#link-to 'yarn-apps.apps' tagName="li" current-when="yarn-apps.apps yarn-apps.services"}}
{{#link-to 'yarn-apps.apps' class="navigation-link" currentWhen="yarn-apps.apps yarn-apps.services"}}Applications {{#link-to 'yarn-apps.apps' class="navigation-link"}}Applications
<span class="sr-only">(current)</span> <span class="sr-only">(current)</span>
{{/link-to}} {{/link-to}}
{{/link-to}} {{/link-to}}
{{#link-to 'yarn-nodes.table' tagName="li" currentWhen="yarn-nodes.table yarn-nodes.heatmap"}} {{#link-to 'yarn-services' tagName="li"}}
{{#link-to 'yarn-nodes.table' class="navigation-link" currentWhen="yarn-nodes.table yarn-nodes.heatmap"}}Nodes {{#link-to 'yarn-services' class="navigation-link"}}Services
<span class="sr-only">(current)</span>
{{/link-to}}
{{/link-to}}
{{#link-to 'yarn-nodes.table' tagName="li" current-when="yarn-nodes.table yarn-nodes.heatmap"}}
{{#link-to 'yarn-nodes.table' class="navigation-link"}}Nodes
<span class="sr-only">(current)</span> <span class="sr-only">(current)</span>
{{/link-to}} {{/link-to}}
{{/link-to}} {{/link-to}}

View File

@ -20,7 +20,7 @@
<tbody> <tbody>
<tr> <tr>
<td>Application Attempt Id</td> <td>Application Attempt Id</td>
<td>{{attempt.id}}</td> <td title="{{attempt.id}}">{{attempt.id}}</td>
</tr> </tr>
<tr> <tr>
<td>Started Time</td> <td>Started Time</td>
@ -38,11 +38,11 @@
</tr> </tr>
<tr> <tr>
<td>AM Container Id</td> <td>AM Container Id</td>
<td>{{attempt.appMasterContainerId}}</td> <td title="{{attempt.appMasterContainerId}}">{{attempt.appMasterContainerId}}</td>
</tr> </tr>
<tr> <tr>
<td>AM Node Id</td> <td>AM Node Id</td>
<td>{{attempt.amNodeId}}</td> <td title="{{attempt.amNodeId}}">{{attempt.amNodeId}}</td>
</tr> </tr>
{{#if attempt.attemptState}} {{#if attempt.attemptState}}
<tr> <tr>
@ -53,7 +53,7 @@
{{#if attempt.nodeHttpAddress}} {{#if attempt.nodeHttpAddress}}
<tr> <tr>
<td>AM Node Web UI</td> <td>AM Node Web UI</td>
<td><a href="{{prepend-protocol attempt.nodeHttpAddress}}" target="_blank">{{attempt.nodeHttpAddress}}</a></td> <td title="{{attempt.nodeHttpAddress}}"><a href="{{prepend-protocol attempt.nodeHttpAddress}}" target="_blank">{{attempt.nodeHttpAddress}}</a></td>
</tr> </tr>
{{/if}} {{/if}}
{{#if attempt.logsLink}} {{#if attempt.logsLink}}

View File

@ -51,7 +51,7 @@
{{#if container.nodeHttpAddress}} {{#if container.nodeHttpAddress}}
<tr> <tr>
<td>NodeManager UI</td> <td>NodeManager UI</td>
<td><a href="{{prepend-protocol container.nodeHttpAddress}}" target="_blank">{{container.nodeHttpAddress}}</a></td> <td title="{{container.nodeHttpAddress}}"><a href="{{prepend-protocol container.nodeHttpAddress}}" target="_blank">{{container.nodeHttpAddress}}</a></td>
</tr> </tr>
{{/if}} {{/if}}
{{#if container.logUrl}} {{#if container.logUrl}}

View File

@ -0,0 +1,23 @@
{{!
* 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.
}}
{{#if content}}
{{{content}}}
{{else}}
<span class="txt-message">Not Available!</span>
{{/if}}

View File

@ -38,13 +38,13 @@
<div class="tab-content"> <div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="graphViewTab"> <div role="tabpanel" class="tab-pane active" id="graphViewTab">
<br/><br/> <br/><br/>
<div class="col-md-8 container-fluid" id={{parent-id}}></div> <div class="col-md-7 container-fluid" id={{parent-id}}></div>
<!-- diag info --> <!-- diag info -->
<div class="col-md-4 container-fluid"> <div class="col-md-5 container-fluid">
<div class="panel panel-default add-ellipsis"> <div class="panel panel-default add-ellipsis attempt-info-panel">
<div class="panel-heading"> <div class="panel-heading">
{{#if selected.link}} {{#if selected.link}}
{{#link-to selected.linkname selected.id}}{{selected.id}}{{/link-to}} {{#link-to selected.linkname selected.id (query-params service=serviceName)}}{{selected.id}}{{/link-to}}
{{else}} {{else}}
{{selected.id}} {{selected.id}}
{{/if}} {{/if}}

View File

@ -30,11 +30,11 @@
<ul class="nav nav-pills nav-stacked" id="stacked-menu"> <ul class="nav nav-pills nav-stacked" id="stacked-menu">
<ul class="nav nav-pills nav-stacked collapse in"> <ul class="nav nav-pills nav-stacked collapse in">
{{#link-to 'yarn-app' tagName="li"}} {{#link-to 'yarn-app' tagName="li"}}
{{#link-to 'yarn-app' model.appId}}Information {{#link-to 'yarn-app' model.appId (query-params service=service)}}Information
{{/link-to}} {{/link-to}}
{{/link-to}} {{/link-to}}
{{#link-to 'yarn-app-attempts' tagName="li"}} {{#link-to 'yarn-app-attempts' tagName="li"}}
{{#link-to 'yarn-app-attempts' model.appId}}Attempts List {{#link-to 'yarn-app-attempts' model.appId (query-params service=service)}}Attempts List
{{/link-to}} {{/link-to}}
{{/link-to}} {{/link-to}}
</ul> </ul>
@ -46,7 +46,7 @@
<div class="col-md-10 container-fluid"> <div class="col-md-10 container-fluid">
<!-- timeline view of children --> <!-- timeline view of children -->
<div class="row"> <div class="row">
{{timeline-view parent-id="attempt-timeline-div" my-id="timeline-view" height="100%" rmModel=model.attempts label="shortAppAttemptId" attemptModel=true}} {{timeline-view parent-id="attempt-timeline-div" my-id="timeline-view" height="100%" rmModel=model.attempts label="shortAppAttemptId" attemptModel=true serviceName=service}}
</div> </div>
</div> </div>

View File

@ -25,17 +25,21 @@
<div class="col-md-2 container-fluid"> <div class="col-md-2 container-fluid">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h4>Application</h4> {{#if service}}
<h4>Service</h4>
{{else}}
<h4>Application</h4>
{{/if}}
</div> </div>
<div class="panel-body"> <div class="panel-body">
<ul class="nav nav-pills nav-stacked" id="stacked-menu"> <ul class="nav nav-pills nav-stacked" id="stacked-menu">
<ul class="nav nav-pills nav-stacked collapse in"> <ul class="nav nav-pills nav-stacked collapse in">
{{#link-to 'yarn-app' tagName="li"}} {{#link-to 'yarn-app' tagName="li"}}
{{#link-to 'yarn-app' model.app.id}}Information {{#link-to 'yarn-app' model.app.id (query-params service=service)}}Information
{{/link-to}} {{/link-to}}
{{/link-to}} {{/link-to}}
{{#link-to 'yarn-app-attempts' tagName="li"}} {{#link-to 'yarn-app-attempts' tagName="li"}}
{{#link-to 'yarn-app-attempts' model.app.id}}Attempts List {{#link-to 'yarn-app-attempts' model.app.id (query-params service=service)}}Attempts List
{{/link-to}} {{/link-to}}
{{/link-to}} {{/link-to}}
</ul> </ul>
@ -88,7 +92,7 @@
</span> </span>
</td> </td>
<td>{{model.app.startTime}}</td> <td>{{model.app.startTime}}</td>
<td>{{model.app.elapsedTime}}</td> <td>{{model.app.formattedElapsedTime}}</td>
<td>{{model.app.validatedFinishedTs}}</td> <td>{{model.app.validatedFinishedTs}}</td>
<td>{{model.app.priority}}</td> <td>{{model.app.priority}}</td>
<td> <td>

View File

@ -33,10 +33,6 @@
{{#link-to 'yarn-apps.apps'}}All Applications {{#link-to 'yarn-apps.apps'}}All Applications
{{/link-to}} {{/link-to}}
{{/link-to}} {{/link-to}}
{{#link-to 'yarn-apps.services' tagName="li"}}
{{#link-to 'yarn-apps.services'}}Long Running Services
{{/link-to}}
{{/link-to}}
</ul> </ul>
</ul> </ul>
</div> </div>

View File

@ -0,0 +1,86 @@
{{!--
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.
--}}
{{breadcrumb-bar breadcrumbs=breadcrumbs}}
<div class="col-md-12 container-fluid">
<div class="row">
<div class="col-md-2 container-fluid">
<div class="panel panel-default">
<div class="panel-heading">
<h4>Services</h4>
</div>
<div class="panel-body">
<ul class="nav nav-pills nav-stacked" id="stacked-menu">
<ul class="nav nav-pills nav-stacked collapse in">
{{#link-to 'yarn-services' tagName="li"}}
{{#link-to 'yarn-services'}}Long Running Services
{{/link-to}}
{{/link-to}}
</ul>
</ul>
</div>
</div>
</div>
<div class="col-md-10 container-fluid">
<div class="row">
<div class="col-lg-4 container-fluid">
<div class="panel panel-default">
<div class="panel-heading">
Finished Services
</div>
<div class="container-fluid" id="finishedapps-donut-chart">
{{donut-chart data=getFinishedServicesDataForDonutChart
showLabels=true
parentId="finishedapps-donut-chart"
ratio=0.6
maxHeight=350
colorTargets="good warn error"
}}
</div>
</div>
</div>
<div class="col-lg-4 container-fluid">
<div class="panel panel-default">
<div class="panel-heading">
Running Services
</div>
<div class="container-fluid" id="runningapps-donut-chart">
{{donut-chart data=getRunningServicesDataForDonutChart
showLabels=true
parentId="runningapps-donut-chart"
ratio=0.6
maxHeight=350
colorTargets="warn good"
}}
</div>
</div>
</div>
</div>
{{#if model.apps}}
{{em-table columns=serviceColumns rows=model.apps definition=tableDefinition}}
{{else}}
<h4 align="center">Could not find any services from this cluster</h4>
{{/if}}
</div>
</div>
</div>

View File

@ -125,5 +125,29 @@ export default {
unit = "PB"; unit = "PB";
} }
return value.toFixed(1) + " " + unit; return value.toFixed(1) + " " + unit;
},
msToElapsedTimeUnit: function(millisecs) {
var seconds = Math.floor(millisecs / 1000);
var days = Math.floor(seconds / (3600 * 24));
var hours = Math.floor(seconds / 3600) - (days * 24);
var mins = Math.floor((seconds - (hours * 3600) - (days * 24 * 3600)) / 60);
var secs = seconds - (days * 24 * 3600) - (hours * 3600) - (mins * 60);
var timeStrArr = [];
var pluralize = "";
if (days > 0) {
pluralize = days > 1? " Days" : " Day";
timeStrArr.push(days + pluralize);
}
if (hours > 0) {
pluralize = hours > 1? " Hrs" : " Hour";
timeStrArr.push(hours + pluralize);
}
if (mins > 0) {
pluralize = mins > 1? " Mins" : " Min";
timeStrArr.push(mins + pluralize);
}
pluralize = secs > 1? " Secs" : " Sec";
timeStrArr.push(secs + pluralize);
return timeStrArr.join(" : ");
} }
}; };

View File

@ -0,0 +1,43 @@
/**
* 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 { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('em-table-html-cell', 'Integration | Component | em table html cell', {
integration: true
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
this.render(hbs`{{em-table-html-cell}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:" + EOL +
this.render(hbs`
{{#em-table-html-cell}}
template block text
{{/em-table-html-cell}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View File

@ -0,0 +1,29 @@
/**
* 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 { moduleFor, test } from 'ember-qunit';
moduleFor('route:yarn-services', 'Unit | Route | yarn services', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});