TEZ-2236. Tez UI: Support loading of all tasks in the dag tasks page (Sreenath Somarajapuram via pramachandran)
Project: http://git-wip-us.apache.org/repos/asf/tez/repo Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/9316fe62 Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/9316fe62 Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/9316fe62 Branch: refs/heads/TEZ-2003 Commit: 9316fe627a9153a5caf8c653e97300c7b4bb8095 Parents: 62a348c Author: Prakash Ramachandran <[email protected]> Authored: Wed Apr 8 01:06:50 2015 +0530 Committer: Prakash Ramachandran <[email protected]> Committed: Wed Apr 8 01:06:50 2015 +0530 ---------------------------------------------------------------------- CHANGES.txt | 1 + tez-ui/src/main/webapp/app/scripts/app.js | 5 +- .../basic-table/basic-table-component.js | 91 ++++ .../scripts/components/basic-table/cell-view.js | 43 ++ .../components/basic-table/column-definition.js | 46 ++ .../components/basic-table/header-cell-view.js | 52 +++ .../components/basic-table/pagination-view.js | 62 +++ .../webapp/app/scripts/controllers/dag_tasks.js | 247 ++++------- .../controllers/table-page-controller.js | 39 ++ .../app/scripts/helpers/handlebarHelpers.js | 8 + .../src/main/webapp/app/scripts/helpers/misc.js | 60 ++- .../webapp/app/scripts/helpers/misc.js.orig | 436 +++++++++++++++++++ .../scripts/mixins/data-array-loader-minxin.js | 117 +++++ .../src/main/webapp/app/scripts/models/dag.js | 4 + tez-ui/src/main/webapp/app/scripts/router.js | 15 +- .../scripts/views/extra-table-buttons-view.js | 23 + tez-ui/src/main/webapp/app/styles/colors.less | 7 +- tez-ui/src/main/webapp/app/styles/main.less | 225 +++++++++- tez-ui/src/main/webapp/app/styles/shared.less | 24 + .../main/webapp/app/templates/common/table.hbs | 47 ++ .../app/templates/components/basic-table.hbs | 58 +++ .../components/basic-table/basic-cell.hbs | 19 + .../components/basic-table/header-cell.hbs | 24 + .../components/basic-table/linked-cell.hbs | 25 ++ .../components/basic-table/logs-cell.hbs | 35 ++ .../components/basic-table/pagination-view.hbs | 38 ++ .../components/basic-table/status-cell.hbs | 29 ++ .../basic-table/task-actions-cell.hbs | 26 ++ .../app/templates/views/extra-table-buttons.hbs | 19 + 29 files changed, 1641 insertions(+), 184 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 0eb02a7..f7295f2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,6 +9,7 @@ INCOMPATIBLE CHANGES TEZ-1993. Implement a pluggable InputSizeEstimator for grouping fairly ALL CHANGES: + TEZ-2236. Tez UI: Support loading of all tasks in the dag tasks page TEZ-2159. Tez UI: download timeline data for offline use. TEZ-2269. DAGAppMaster becomes unresponsive (post TEZ-2149). TEZ-2243. documentation should explicitly specify protobuf 2.5.0. http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/app.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/app.js b/tez-ui/src/main/webapp/app/scripts/app.js index 1a2af0e..1d8036d 100644 --- a/tez-ui/src/main/webapp/app/scripts/app.js +++ b/tez-ui/src/main/webapp/app/scripts/app.js @@ -183,8 +183,9 @@ require('scripts/router'); require('scripts/views/**/*'); require('scripts/models/**/*'); +require('scripts/controllers/table-page-controller'); require('scripts/controllers/**/*'); -require('scripts/components/*'); -require('scripts/components/dag-view/*'); +require('scripts/components/basic-table/basic-table-component'); +require('scripts/components/**/*'); require('scripts/adapters/*'); http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/components/basic-table/basic-table-component.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/components/basic-table/basic-table-component.js b/tez-ui/src/main/webapp/app/scripts/components/basic-table/basic-table-component.js new file mode 100644 index 0000000..5a47c78 --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/components/basic-table/basic-table-component.js @@ -0,0 +1,91 @@ +/** + * 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. + */ + +App.BasicTableComponent = Em.Component.extend({ + layoutName: 'components/basic-table', + + statusMessage: null, + internalStatusMessage: null, + + pageNum: 1, + rowCount: 10, + rowCountOptions: [5, 10, 25, 50, 100], + pageNavOnFooterAt: 25, + + totalPages: function () { + return Math.ceil(this.get('rows.length') / this.get('rowCount')); + }.property('rows.length', 'rowCount'), + + hasPageNavOnFooter: function () { + return this.get('enablePagination') && this.get('_rows.length') >= this.get('pageNavOnFooterAt'); + }.property('enablePagination', '_rows.length', 'pageNavOnFooterAt'), + + _showHeader: function () { + return this.get('enablePagination') || + this.get('extraHeaderItem') || + this.get('_statusMessage'); + }.property('enablePagination', 'extraHeaderItem', '_statusMessage'), + + _statusMessage: function() { + return this.get('internalStatusMessage') || this.get('statusMessage'); + }.property('internalStatusMessage', 'statusMessage'), + + _pageNumResetObserver: function () { + this.set('pageNum', 1); + }.observes('rowCount'), + + _columns: function () { + var columns = this.get('columns'), + widthPercentageToFit = 100 / columns.length; + + columns.map(function (column) { + var templateName = column.get('templateName'), + cellOptions = { + column: column + }; + + if(templateName) { + cellOptions.templateName = templateName; + } + + column.setProperties({ + width: widthPercentageToFit + "%", + cellView: App.BasicTableComponent.CellView.extend(cellOptions), + headerCellView: App.BasicTableComponent.HeaderCellView.extend({ + column: column, + table: this + }) + }); + }); + + return columns; + }.property('columns'), + + _rows: function () { + var startIndex = (this.get('pageNum') - 1) * this.get('rowCount'); + return this.get('rows').slice(startIndex, startIndex + this.get('rowCount')); + }.property('rows.@each', 'rowCount', 'pageNum'), + + actions: { + changePage: function (pageNum) { + this.set('pageNum', pageNum); + } + } +}); + +Em.Handlebars.helper('basic-table-component', App.BasicTableComponent); http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/components/basic-table/cell-view.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/components/basic-table/cell-view.js b/tez-ui/src/main/webapp/app/scripts/components/basic-table/cell-view.js new file mode 100644 index 0000000..4d50cf1 --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/components/basic-table/cell-view.js @@ -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. + */ + +var ObjectPromiseController = Ember.ObjectController.extend(Ember.PromiseProxyMixin), + naCellContent = { + toString: function (){ + return 'Not available!'; + }, + _notAvailable: true + }; + +App.BasicTableComponent.CellView = Ember.View.extend({ + templateName: 'components/basic-table/basic-cell', + + classNames: ['cell-content'], + + cellContent: function () { + var cellContent = this.get('column').getCellContent(this.get('row')); + + if(cellContent && $.isFunction(cellContent.then)) { + cellContent = ObjectPromiseController.create({ + promise: cellContent + }); + } + + return cellContent || naCellContent; + }.property('row', 'column') +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/components/basic-table/column-definition.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/components/basic-table/column-definition.js b/tez-ui/src/main/webapp/app/scripts/components/basic-table/column-definition.js new file mode 100644 index 0000000..fa0a47d --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/components/basic-table/column-definition.js @@ -0,0 +1,46 @@ +/** + * 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. + */ + +App.BasicTableComponent.ColumnDefinition = (function () { + function getContentAtPath(row) { + var contentPath = this.get('contentPath'); + + if(contentPath) { + return row.get(contentPath); + } + else { + throw new Error("contentPath not set!"); + } + } + + return Em.Object.extend({ + contentPath: null, + headerCellName: "Not Available!", + + width: "", + + customStyle: function () { + return 'width:%@'.fmt(this.get('width')); + }.property('width'), + + getSearchValue: getContentAtPath, + getSortValue: getContentAtPath, + getCellContent: getContentAtPath + }); +})(); + http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/components/basic-table/header-cell-view.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/components/basic-table/header-cell-view.js b/tez-ui/src/main/webapp/app/scripts/components/basic-table/header-cell-view.js new file mode 100644 index 0000000..d6773d6 --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/components/basic-table/header-cell-view.js @@ -0,0 +1,52 @@ +/** + * 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. + */ + +App.BasicTableComponent.HeaderCellView = Ember.View.extend({ + templateName: 'components/basic-table/header-cell', + + _onColResize: function (event) { + var data = event.data; + + if(!data.startEvent) { + data.startEvent = event; + } + + data.thisHeader.set( + 'column.width', + (data.startWidth + event.clientX - data.startEvent.clientX) + 'px' + ); + }, + + _endColResize: function (event) { + var thisHeader = event.data.thisHeader; + $(document).off('mousemove', thisHeader._onColResize); + $(document).off('mouseup', thisHeader._endColResize); + }, + + actions: { + startColResize: function () { + var mouseTracker = { + thisHeader: this, + startWidth: $(this.get('element')).width(), + startEvent: null + }; + $(document).on('mousemove', mouseTracker, this._onColResize); + $(document).on('mouseup', mouseTracker, this._endColResize); + } + } +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/components/basic-table/pagination-view.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/components/basic-table/pagination-view.js b/tez-ui/src/main/webapp/app/scripts/components/basic-table/pagination-view.js new file mode 100644 index 0000000..4f136b1 --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/components/basic-table/pagination-view.js @@ -0,0 +1,62 @@ +/** + * 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. + */ + +App.BasicTableComponent.PaginationView = Ember.View.extend({ + templateName: 'components/basic-table/pagination-view', + + classNames: ['pagination-view'], + + atFirst: function () { + return this.get('pageNum') == 1; + }.property('pageNum'), + + atLast: function () { + return this.get('pageNum') == this.get('totalPages'); + }.property('pageNum', 'totalPages'), + + _possiblePages: function () { + var pageNum = this.get('pageNum'), + totalPages = this.get('totalPages'), + possiblePages = [], + startPage = 1, + endPage = totalPages, + delta = 0; + + if(totalPages > 5) { + startPage = pageNum - 2, endPage = pageNum + 2; + + if(startPage < 1) { + delta = 1 - startPage; + } + else if(endPage > totalPages) { + delta = totalPages - endPage; + } + + startPage += delta, endPage += delta; + } + + while(startPage <= endPage) { + possiblePages.push({ + isCurrent: startPage == pageNum, + pageNum: startPage++ + }); + } + + return possiblePages; + }.property('pageNum', 'totalPages'), +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js b/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js index 519f06d..267eac5 100644 --- a/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js +++ b/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js @@ -16,185 +16,133 @@ * limitations under the License. */ -App.DagTasksController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, { - needs: "dag", +App.DagTasksController = App.TablePageController.extend({ controllerName: 'DagTasksController', + needs: "dag", - // required by the PaginatedContentMixin - childEntityType: 'task', + entityType: 'task', + filterEntityType: 'dag', + filterEntityId: Ember.computed.alias('controllers.dag.id'), - queryParams: { - status_filter: 'status', - vertex_name_filter: 'vertex_name', + beforeLoad: function () { + var dagController = this.get('controllers.dag'), + model = dagController.get('model'); + return model.reload().then(function () { + return dagController.loadAdditional(model); + }); }, - status_filter: null, - vertex_name_filter: null, - loadData: function() { - var primaryFilter = { - TEZ_DAG_ID : this.get('controllers.dag.id') - }; - if (!!this.vertex_name_filter) { - var vertexIdMap = this.get('controllers.dag.vertexIdToNameMap'); - var vertexId = App.Helpers.misc.getVertexIdFromName(vertexIdMap, this.vertex_name_filter) - || 'unknown'; - primaryFilter = { TEZ_VERTEX_ID : vertexId }; - } + afterLoad: function () { + var data = this.get('data'), + isUnsuccessfulDag = App.Helpers.misc.isStatusInUnsuccessful( + this.get('controllers.dag.status') + ); + + data.forEach(function (task) { + var taskStatus = App.Helpers.misc.getFixedupDisplayStatus(task.get('status')); - var filters = { - primary: primaryFilter, - secondary: { - status: this.status_filter + if (taskStatus == 'RUNNING' && isUnsuccessfulDag) { + taskStatus = 'KILLED' } - } - this.setFiltersAndLoadEntities(filters); + if (taskStatus != task.get('status')) { + task.set('status', taskStatus); + } + }); + + return this._super(); }, - load: function () { - var dag = this.get('controllers.dag.model'), - controller = this.get('controllers.dag'), - t = this; - t.set('loading', true); - dag.reload().then(function () { - return controller.loadAdditional(dag); - }).then(function () { - t.resetNavigation(); - t.loadEntities(); - }).catch(function(error){ - Em.Logger.error(error); - var err = App.Helpers.misc.formatError(error, defaultErrMsg); - var msg = 'error code: %@, message: %@'.fmt(err.errCode, err.msg); - App.Helpers.ErrorBar.getInstance().show(msg, err.details); - }); - }.observes('count'), + columns: function() { + var visibleColumnConfigs = this.get('columnConfigs').filter(function (column) { + return this.visibleColumnIds[column.id]; + }, this); + + return App.Helpers.misc.createColumnDescription(visibleColumnConfigs); + }.property('visibleColumnIds'), - loadEntities: function() { + defaultColumnConfigs: function() { var that = this, - store = this.get('store'), - fetcher; - childEntityType = this.get('childEntityType'); - var defaultErrMsg = 'Error while loading tasks.'; + vertexIdToNameMap = this.get('controllers.dag.vertexIdToNameMap') || {}; - that.set('loading', true); - store.unloadAll(childEntityType); - store.findQuery(childEntityType, this.getFilterProperties()) - .then(function(entities){ + function getLogContent(attempt) { + var yarnAppState = that.get('controllers.dag.yarnAppState'), + suffix, + link, + cellContent = {}; - var pivotLoaders = []; - var dagStatus = that.get('controllers.dag.status'); - entities.forEach(function (task) { - var taskStatus = App.Helpers.misc - .getFixedupDisplayStatus(task.get('status')); - if (taskStatus == 'RUNNING' && - App.Helpers.misc.isStatusInUnsuccessful(dagStatus)) { - taskStatus = 'KILLED' - } - if (taskStatus != task.get('status')) { - task.set('status', taskStatus); + if(attempt) { + suffix = "/syslog_" + attempt.get('id'), + link = attempt.get('inProgressLog') || attempt.get('completedLog'); + + if(link) { + cellContent.viewUrl = link + suffix; } - var taskAttemptId = task.get('successfulAttemptId') || - task.get('attempts.lastObject'); - if (!!taskAttemptId) { - // Pivot attempt selection logic - App.Helpers.misc.removeRecord(store, 'taskAttempt', taskAttemptId); - fetcher = store.find('taskAttempt', taskAttemptId); - fetcher.then(function (attempt) { - task.set('pivotAttempt', attempt); - }); - pivotLoaders.push(fetcher); + link = attempt.get('completedLog'); + if (link && yarnAppState === 'FINISHED' || yarnAppState === 'KILLED' || yarnAppState === 'FAILED') { + cellContent.downloadUrl = link + suffix; } - }); - Em.RSVP.allSettled(pivotLoaders).then(function(){ - that.set('entities', entities); - that.set('loading', false); - }); - }).catch(function(error){ - Em.Logger.error(error); - var err = App.Helpers.misc.formatError(error, defaultErrMsg); - var msg = 'error code: %@, message: %@'.fmt(err.errCode, err.msg); - App.Helpers.ErrorBar.getInstance().show(msg, error.details); - }); - }, - - actions : { - filterUpdated: function(filterID, value) { - // any validations required goes here. - if (!!value) { - this.set(filterID, value); - } else { - this.set(filterID, null); } - this.loadData(); + + cellContent.notAvailable = cellContent.viewUrl || cellContent.downloadUrl; + + return cellContent; } - }, - defaultColumnConfigs: function() { - var that = this, - vertexIdToNameMap = this.get('controllers.dag.vertexIdToNameMap') || {}; return [ { id: 'id', headerCellName: 'Task Index', - tableCellViewClass: Em.Table.TableCell.extend({ - template: Em.Handlebars.compile( - "{{#link-to 'task' view.cellContent.id class='ember-table-content'}}{{view.cellContent.displayId}}{{/link-to}}") - }), + templateName: 'components/basic-table/linked-cell', + contentPath: 'id', getCellContent: function (row) { var id = row.get('id'), idPrefix = 'task_%@_'.fmt(row.get('dagID').substr(4)); return { - id: id, - displayId: id.indexOf(idPrefix) == 0 ? id.substr(idPrefix.length) : id + linkTo: 'task', + entityId: id, + displayText: id.indexOf(idPrefix) == 0 ? id.substr(idPrefix.length) : id }; - } + }, }, { id: 'vertexName', headerCellName: 'Vertex Name', - filterID: 'vertex_name_filter', + contentPath: 'vertexID', getCellContent: function(row) { var vertexId = row.get('vertexID'); return vertexIdToNameMap[vertexId] || vertexId; - } + }, }, { id: 'startTime', headerCellName: 'Start Time', + contentPath: 'startTime', getCellContent: function(row) { return App.Helpers.date.dateFormat(row.get('startTime')); - } + }, }, { id: 'endTime', headerCellName: 'End Time', + contentPath: 'endTime', getCellContent: function(row) { return App.Helpers.date.dateFormat(row.get('endTime')); - } + }, }, { id: 'duration', headerCellName: 'Duration', + contentPath: 'duration', getCellContent: function(row) { - var st = row.get('startTime'); - var et = row.get('endTime'); - if (st && et) { - return App.Helpers.date.durationSummary(st, et); - } - } + return App.Helpers.date.timingFormat(row.get('duration'), 1); + }, }, { id: 'status', headerCellName: 'Status', - filterID: 'status_filter', - filterType: 'dropdown', - dropdownValues: App.Helpers.misc.taskStatusUIOptions, - tableCellViewClass: Em.Table.TableCell.extend({ - template: Em.Handlebars.compile( - '<span class="ember-table-content"> \ - <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\ - {{view.cellContent.status}}</span>') - }), + templateName: 'components/basic-table/status-cell', + contentPath: 'status', getCellContent: function(row) { var status = row.get('status'); return { @@ -206,59 +154,20 @@ App.DagTasksController = Em.ObjectController.extend(App.PaginatedContentMixin, A { id: 'actions', headerCellName: 'Actions', - tableCellViewClass: Em.Table.TableCell.extend({ - template: Em.Handlebars.compile( - '<span class="ember-table-content">\ - {{#link-to "task.counters" view.cellContent}}counters{{/link-to}} \ - {{#link-to "task.attempts" view.cellContent}}attempts{{/link-to}}\ - </span>' - ) - }), - contentPath: 'id' + templateName: 'components/basic-table/task-actions-cell', + contentPath: 'id', }, { id: 'logs', - textAlign: 'text-align-left', headerCellName: 'Logs', - tableCellViewClass: Em.Table.TableCell.extend({ - template: Em.Handlebars.compile( - '<span class="ember-table-content">\ - {{#unless view.cellContent.notAvailable}}\ - Not Available\ - {{else}}\ - {{#if view.cellContent.viewUrl}}\ - <a target="_blank" href="//{{unbound view.cellContent.viewUrl}}">View</a>\ - \ - {{/if}}\ - {{#if view.cellContent.downloadUrl}}\ - <a target="_blank" href="{{unbound view.cellContent.downloadUrl}}?start=0" download type="application/octet-stream">Download</a>\ - {{/if}}\ - {{/unless}}\ - </span>') - }), + templateName: 'components/basic-table/logs-cell', getCellContent: function(row) { - var yarnAppState = that.get('controllers.dag.yarnAppState'), - attempt = row.get('pivotAttempt'), - suffix, - link, - cellContent = {}; - - if(attempt) { - suffix = "/syslog_" + attempt.get('id'), - link = attempt.get('inProgressLog') || attempt.get('completedLog'); + var taskAttemptId = row.get('successfulAttemptId') || row.get('attempts.lastObject'), + store = that.get('store'); - if(link) { - cellContent.viewUrl = link + suffix; - } - link = attempt.get('completedLog'); - if (link && yarnAppState === 'FINISHED' || yarnAppState === 'KILLED' || yarnAppState === 'FAILED') { - cellContent.downloadUrl = link + suffix; - } + if (taskAttemptId) { + return store.find('taskAttempt', taskAttemptId).then(getLogContent); } - - cellContent.notAvailable = cellContent.viewUrl || cellContent.downloadUrl; - - return cellContent; } } ]; http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/controllers/table-page-controller.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/table-page-controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/table-page-controller.js new file mode 100644 index 0000000..002c6b5 --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/controllers/table-page-controller.js @@ -0,0 +1,39 @@ +/** + * 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. + */ + +App.TablePageController = Em.ObjectController.extend( + App.DataArrayLoaderMixin, + App.ColumnSelectorMixin, { + queryParams: ['pageNum', 'rowCount'], + + pageNum: 1, + rowCount: 25, + + isRefreshable: true, + + statusMessage: function () { + return this.get('loading') ? "Loading all records..." : null; + }.property('loading'), + + actions: { + refresh: function () { + this.loadData(true); + } + } + } +); http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/helpers/handlebarHelpers.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/helpers/handlebarHelpers.js b/tez-ui/src/main/webapp/app/scripts/helpers/handlebarHelpers.js index 0cd6b9d..5fe475f 100644 --- a/tez-ui/src/main/webapp/app/scripts/helpers/handlebarHelpers.js +++ b/tez-ui/src/main/webapp/app/scripts/helpers/handlebarHelpers.js @@ -72,3 +72,11 @@ Em.Handlebars.helper('formatDiagnostics', function(diagnostics) { x = replaceAll(x, ']', '</div>'); return new Handlebars.SafeString(x); }); + +/** + * Returns first-item class if called from inside a loop/each. + * @param view Will be _view in hbs + */ +Em.Handlebars.helper('firstItemCSS', function(view) { + return view && view.contentIndex == 0 ? 'first-item' : ''; +}); http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/helpers/misc.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/helpers/misc.js b/tez-ui/src/main/webapp/app/scripts/helpers/misc.js index fdd69bd..27275dd 100644 --- a/tez-ui/src/main/webapp/app/scripts/helpers/misc.js +++ b/tez-ui/src/main/webapp/app/scripts/helpers/misc.js @@ -115,7 +115,10 @@ App.Helpers.misc = { configuration.headerCellName = configuration.counterName || configuration.counterId; configuration.id = '%@/%@'.fmt(configuration.counterGroupName || configuration.groupId, configuration.counterName || configuration.counterId), - configuration.getCellContent = App.Helpers.misc.getCounterCellContent; + + configuration.getSortValue = App.Helpers.misc.getCounterCellContent; + configuration.getCellContent = + configuration.getSearchValue = App.Helpers.misc.getCounterCellContentFormatted; return configuration; }); }, @@ -138,6 +141,12 @@ App.Helpers.misc = { }); }, + createColumnDescription: function (columnConfigs) { + return columnConfigs.map(function (column) { + return App.BasicTableComponent.ColumnDefinition.create(column); + }); + }, + /* * Returns a counter value from for a row * @param row @@ -145,19 +154,26 @@ App.Helpers.misc = { */ getCounterCellContent: function (row) { var contentPath = this.id.split('/'), - group = contentPath[0], - counter = contentPath[1], - id = row.get('id'), - value = 'Not Available'; + value = null; try{ value = row.get('counterGroups'). - findBy('id', '%@/%@'.fmt(id, group)). - get('counters'). - findBy('id', '%@/%@/%@'.fmt(id, group, counter)). - get('value'); + findBy('counterGroupName', contentPath[0]) + ['counters']. + findBy('counterName', contentPath[1]) + ['counterValue']; }catch(e){} + return value; + }, + + /* + * Returns a counter value from for a row + * @param row + * @return value + */ + getCounterCellContentFormatted: function (row) { + var value = App.Helpers.misc.getCounterCellContent.call(this, row); return App.Helpers.number.formatNumThousands(value); }, @@ -393,6 +409,32 @@ App.Helpers.misc = { } }, + timelinePathForType: (function () { + var typeToPathMap = { + dag: 'TEZ_DAG_ID', + vertex: 'TEZ_VERTEX_ID', + task: 'TEZ_TASK_ID', + taskAttempt: 'TEZ_TASK_ATTEMPT_ID', + tezApp: 'TEZ_APPLICATION' + }; + return function (type) { + return typeToPathMap[type]; + }; + })(), + + getTimelineFilterForType: (function () { + var typeToPathMap = { + dag: 'TEZ_DAG_ID', + vertex: 'TEZ_VERTEX_ID', + task: 'TEZ_TASK_ID', + taskAttempt: 'TEZ_TASK_ATTEMPT_ID', + tezApp: 'applicationId' + }; + return function (type) { + return typeToPathMap[type]; + }; + })(), + dagStatusUIOptions: [ { label: 'All', id: null }, { label: 'Submitted', id: 'SUBMITTED' }, http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/helpers/misc.js.orig ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/helpers/misc.js.orig b/tez-ui/src/main/webapp/app/scripts/helpers/misc.js.orig new file mode 100644 index 0000000..fdd69bd --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/helpers/misc.js.orig @@ -0,0 +1,436 @@ +/** + * 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. + */ + +App.Helpers.misc = { + getStatusClassForEntity: function(status, hasFailedTasks) { + if(!status) return ''; + + switch(status) { + case 'FAILED': + return 'failed'; + case 'KILLED': + return 'killed'; + case 'RUNNING': + return 'running'; + case 'ERROR': + return 'error'; + case 'SUCCEEDED': + if (!!hasFailedTasks) { + return 'warning'; + } + /* + TODO: TEZ-2113 + var counterGroups = dag.get('counterGroups'); + var numFailedTasks = this.getCounterValueForDag(counterGroups, + dag.get('id'), 'org.apache.tez.common.counters.DAGCounter', + 'NUM_FAILED_TASKS' + ); + + if (numFailedTasks > 0) { + return 'warning'; + }*/ + + return 'success'; + case 'UNDEFINED': + return 'unknown'; + default: + return 'submitted'; + } + }, + + getRealStatus: function(entityState, yarnAppState, yarnAppFinalState) { + if (entityState != 'RUNNING' || (yarnAppState != 'FINISHED' && yarnAppState != 'KILLED' && yarnAppState != 'FAILED')) { + return entityState; + } + + if (yarnAppState == 'KILLED' || yarnAppState == 'FAILED') { + return yarnAppState; + } + + return yarnAppFinalState; + }, + + getCounterValueForDag: function(counterGroups, dagID, counterGroupName, counterName) { + if (!counterGroups) { + return 0; + } + + var cgName = dagID + '/' + counterGroupName; + var cg = counterGroups.findBy('id', cgName); + if (!cg) { + return 0; + } + var counters = cg.get('counters'); + if (!counters) { + return 0; + } + + var counter = counters.findBy('id', cgName + '/' + counterName); + if (!counter) return 0; + + return counter.get('value'); + }, + + isValidDagStatus: function(status) { + return $.inArray(status, ['SUBMITTED', 'INITING', 'RUNNING', 'SUCCEEDED', + 'KILLED', 'FAILED', 'ERROR']) != -1; + }, + + isValidTaskStatus: function(status) { + return $.inArray(status, ['RUNNING', 'SUCCEEDED', 'FAILED', 'KILLED']) != -1; + }, + + isStatusInUnsuccessful: function(status) { + return $.inArray(status, ['FAILED', 'KILLED', 'UNDEFINED']) != -1; + }, + + /** + * To trim a complete class path with namespace to the class name. + */ + getClassName: function (classPath) { + return classPath.substr(classPath.lastIndexOf('.') + 1); + }, + + /* + * Normalizes counter style configurations + * @param counterConfigs Array + * @return Normalized configurations + */ + normalizeCounterConfigs: function (counterConfigs) { + return counterConfigs.map(function (configuration) { + configuration.headerCellName = configuration.counterName || configuration.counterId; + configuration.id = '%@/%@'.fmt(configuration.counterGroupName || configuration.groupId, + configuration.counterName || configuration.counterId), + configuration.getCellContent = App.Helpers.misc.getCounterCellContent; + return configuration; + }); + }, + + /* + * Creates column definitions form configuration object array + * @param columnConfigs Array + * @return columnDefinitions Array + */ + createColumnsFromConfigs: function (columnConfigs) { + return columnConfigs.map(function (columnConfig) { + if(columnConfig.getCellContentHelper) { + columnConfig.getCellContent = App.Helpers.get(columnConfig.getCellContentHelper); + } + columnConfig.minWidth = columnConfig.minWidth || 135; + + return columnConfig.filterID ? + App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin, columnConfig) : + App.ExTable.ColumnDefinition.create(columnConfig); + }); + }, + + /* + * Returns a counter value from for a row + * @param row + * @return value + */ + getCounterCellContent: function (row) { + var contentPath = this.id.split('/'), + group = contentPath[0], + counter = contentPath[1], + id = row.get('id'), + value = 'Not Available'; + + try{ + value = row.get('counterGroups'). + findBy('id', '%@/%@'.fmt(id, group)). + get('counters'). + findBy('id', '%@/%@/%@'.fmt(id, group, counter)). + get('value'); + }catch(e){} + + return App.Helpers.number.formatNumThousands(value); + }, + + /* + * returns a formatted message, the real cause is unknown and the error object details + * depends on the error cause. the function tries to handle ajax error or a native errors + */ + formatError: function(error, defaultErrorMessage) { + var msg; + // for cross domain requests, the error is not set if no access control headers were found. + // this could be either because there was a n/w error or the cors headers being not set. + if (error.status === 0 && error.statusText === 'error') { + msg = defaultErrorMessage ; + } else { + msg = error.statusText || error.message; + } + msg = msg || 'Unknown error'; + if (!!error.responseText) { + msg += error.responseText; + } + + if(error.requestOptions) { + msg = '%@<br/>Could not retrieve expected data from %@ @ %@'.fmt( + msg, + error.requestOptions.targetServer, + error.requestOptions.url + ) + } + + return { + errCode: error.status || 'Unknown', + msg: msg, + details: error.stack + }; + }, + + /** + * Normalize path + * @param path {String} + * @return normalized path {String} + */ + normalizePath: function (path) { + if(path && path.charAt(path.length - 1) == '/') { + path = path.slice(0, -1); + } + return path; + }, + + // Tez originally shows the status for task and task attempt only on + // completion. this causes confusion to the user as the status would not be + // displayed. so if status is not set return the status as 'RUNNING'. We do + // not diffentiate between running and scheduled. + getFixedupDisplayStatus: function(originalStatus) { + // if status is not set show it as running, since originally the task did + // not have a status set on scheduled/running. + // with the new version we set the status of task as scheduled and that of + // task attempt as running + if (!originalStatus || originalStatus == 'SCHEDULED') { + originalStatus = 'RUNNING'; + } + return originalStatus; + }, + + /** + * Merge content of obj2 into obj2, array elements will be concated. + * @param obj1 {Object} + * @param obj2 {Object} + */ + merge: function objectMerge(obj1, obj2) { + $.each(obj2, function (key, val) { + if(Array.isArray(obj1[key]) && Array.isArray(val)) { + $.merge(obj1[key], val); + } + else if($.isPlainObject(obj1[key]) && $.isPlainObject(val)) { + objectMerge(obj1[key], val); + } + else { + obj1[key] = val; + } + }); + }, + + getTaskIndex: function(dagID, taskID) { + var idPrefix = 'task_%@_'.fmt(dagID.substr(4)); + return taskID.indexOf(idPrefix) == 0 ? taskID.substr(idPrefix.length) : id; + }, + + getVertexIdFromName: function(idToNameMap, vertexName) { + idToNameMap = idToNameMap || {}; + var vertexId = undefined; + $.each(idToNameMap, function(id, name) { + if (name === vertexName) { + vertexId = id; + return false; + } + }); + return vertexId; + }, + + /** + * Remove the specific record from store + * @param store {DS.Store} + * @param type {String} + * @param id {String} + */ + removeRecord: function (store, type, id) { + var record = store.getById(type, id); + if(record) { + store.unloadRecord(record); + } + }, + + downloadDAG: function(dagID, options) { + var opts = options || {}, + batchSize = opts.batchSize || 1000, + baseurl = '%@/%@'.fmt(App.env.timelineBaseUrl, App.Configs.restNamespace.timeline), + itemsToDownload = [ + { + url: getUrl('TEZ_DAG_ID', dagID), + context: { name: 'dag', type: 'TEZ_DAG_ID' }, + onItemFetched: processSingleItem + }, + { + url: getUrl('TEZ_VERTEX_ID', dagID), + context: { name: 'vertices', type: 'TEZ_VERTEX_ID', part: 0 }, + onItemFetched: processMultipleItems + }, + { + url: getUrl('TEZ_TASK_ID', dagID), + context: { name: 'tasks', type: 'TEZ_TASK_ID', part: 0 }, + onItemFetched: processMultipleItems + }, + { + url: getUrl('TEZ_TASK_ATTEMPT_ID', dagID), + context: { name: 'task_attempts', type: 'TEZ_TASK_ATTEMPT_ID', part: 0 }, + onItemFetched: processMultipleItems + } + ], + numItemTypesToDownload = itemsToDownload.length, + downloader = App.Helpers.io.fileDownloader(), + zipHelper = App.Helpers.io.zipHelper({ + onProgress: function(filename, current, total) { + Em.Logger.debug('%@: %@ of %@'.fmt(filename, current, total)); + }, + onAdd: function(filename) { + Em.Logger.debug('adding %@ to Zip'.fmt(filename)); + } + }); + + function getUrl(type, dagID, fromID) { + var url; + if (type == 'TEZ_DAG_ID') { + url = '%@/%@/%@'.fmt(baseurl, type, dagID); + } else { + url = '%@/%@?primaryFilter=TEZ_DAG_ID:%@&limit=%@'.fmt(baseurl, type, dagID, batchSize + 1); + if (!!fromID) { + url = '%@&fromId=%@'.fmt(url, fromID); + } + } + return url; + } + + function checkIfAllDownloaded() { + numItemTypesToDownload--; + if (numItemTypesToDownload == 0) { + downloader.finish(); + } + } + + function processSingleItem(data, context) { + var obj = {}; + obj[context.name] = data; + + zipHelper.addFile({name: '%@.json'.fmt(context.name), data: JSON.stringify(obj, null, 2)}); + checkIfAllDownloaded(); + } + + function processMultipleItems(data, context) { + var obj = {}; + var nextBatchStart = undefined; + + if (!$.isArray(data.entities)) { + throw "invalid data"; + } + + // need to handle no more entries , zero entries + if (data.entities.length > batchSize) { + nextBatchStart = data.entities.pop().entity; + } + obj[context.name] = data.entities; + + zipHelper.addFile({name: '%@_part_%@.json'.fmt(context.name, context.part), data: JSON.stringify(obj, null, 2)}); + + if (!!nextBatchStart) { + context.part++; + downloader.queueItem({ + url: getUrl(context.type, dagID, nextBatchStart), + context: context, + onItemFetched: processMultipleItems + }); + } else { + checkIfAllDownloaded(); + } + } + + downloader.queueItems(itemsToDownload); + + downloader.then(function() { + Em.Logger.info('Finished download'); + zipHelper.close(); + }).catch(function() { + Em.Logger.error('Failed to download'); + zipHelper.abort(); + }); + + var that = this; + zipHelper.then(function(zippedBlob) { + saveAs(zippedBlob, '%@.zip'.fmt(dagID)); + if ($.isFunction(opts.onSuccess)) { + opts.onSuccess(); + } + }).catch(function() { + Em.Logger.error('zip Failed'); + if ($.isFunction(opts.onFailure)) { + opts.onFailure(); + } + }); + + return { + cancel: function() { + downloader.cancel(); + } + } + }, + + dagStatusUIOptions: [ + { label: 'All', id: null }, + { label: 'Submitted', id: 'SUBMITTED' }, + { label: 'Running', id: 'RUNNING' }, + { label: 'Succeeded', id: 'SUCCEEDED' }, + { label: 'Failed', id: 'FAILED' }, + { label: 'Killed', id: 'KILLED' }, + { label: 'Error', id: 'ERROR' }, + ], + + vertexStatusUIOptions: [ + { label: 'All', id: null }, + { label: 'Running', id: 'RUNNING' }, + { label: 'Succeeded', id: 'SUCCEEDED' }, + { label: 'Failed', id: 'FAILED' }, + { label: 'Killed', id: 'KILLED' }, + { label: 'Error', id: 'ERROR' }, + ], + + taskStatusUIOptions: [ + { label: 'All', id: null }, + { label: 'Running', id: 'SCHEDULED' }, + { label: 'Succeeded', id: 'SUCCEEDED' }, + { label: 'Failed', id: 'FAILED' }, + { label: 'Killed', id: 'KILLED' }, + ], + + taskAttemptStatusUIOptions: [ + { label: 'All', id: null }, + { label: 'Running', id: 'RUNNING' }, + { label: 'Succeeded', id: 'SUCCEEDED' }, + { label: 'Failed', id: 'FAILED' }, + { label: 'Killed', id: 'KILLED' }, + ], + + defaultQueryParamsConfig: { + refreshModel: true, + replace: true + } + +} http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/mixins/data-array-loader-minxin.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/mixins/data-array-loader-minxin.js b/tez-ui/src/main/webapp/app/scripts/mixins/data-array-loader-minxin.js new file mode 100644 index 0000000..977143c --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/mixins/data-array-loader-minxin.js @@ -0,0 +1,117 @@ +/** + * 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. + */ + +var dataCache = {}; + +App.DataArrayLoaderMixin = Em.Mixin.create({ + data: [], + loading: false, + + entityType: null, + filterEntityType: null, + filterEntityId: null, + + isRefreshable: true, + + _cacheKey: function () { + return [ + this.get('filterEntityType'), + this.get('filterEntityId'), + this.get('entityType'), + ].join(':'); + }.property('filterEntityType', 'filterEntityId', 'entityType'), + + getFilter: function (limit) { + return { + limit: limit || Number.MAX_SAFE_INTEGER - 1, + primaryFilter: '%@:%@'.fmt( + App.Helpers.misc.getTimelineFilterForType(this.get('filterEntityType')), + this.get('filterEntityId') + ) + }; + }, + + loadData: function (skipCache) { + var data; + + if(this.get('loading')) { + return false; + } + + if(!skipCache) { + data = dataCache[this.get('_cacheKey')]; + } + + if(data && data.get('content.length')) { + this.set('data', data); + } + else { + this.loadAllData(); + } + + return true; + }, + + loadAllData: function () { + this.set('loading', true); + + // Load all rows + return this.beforeLoad(). + then(this.load.bind(this, this.getFilter())). + then(this.afterLoad.bind(this)). + then(this.cacheData.bind(this)). + then(this.set.bind(this, 'loading', false)). + catch(this.errorHandler.bind(this)); + }, + + beforeLoad: function () { + return new Em.RSVP.resolve(); + }, + + load: function (filter) { + var entityType = this.get('entityType'), + store = this.get('store'), + data = dataCache[this.get('_cacheKey')]; + + if(data) { + data.toArray().forEach(function (record) { + record.unloadRecord(); + }); + dataCache[this.get('_cacheKey')] = null; + } + + return store.findQuery(entityType, filter). + then(this.set.bind(this, 'data')). + catch(this.errorHandler.bind(this)); + }, + + cacheData: function () { + dataCache[this.get('_cacheKey')] = this.get('data'); + }, + + errorHandler: function (error) { + Em.Logger.error(error); + var err = App.Helpers.misc.formatError(error, 'Error while loading ' + this.get('entityType')); + var msg = 'Error code: %@, message: %@'.fmt(err.errCode, err.msg); + App.Helpers.ErrorBar.getInstance().show(msg, err.details); + }, + + afterLoad: function () { + return new Em.RSVP.resolve(); + }, +}); http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/models/dag.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/models/dag.js b/tez-ui/src/main/webapp/app/scripts/models/dag.js index b86578c..05e356b 100644 --- a/tez-ui/src/main/webapp/app/scripts/models/dag.js +++ b/tez-ui/src/main/webapp/app/scripts/models/dag.js @@ -333,6 +333,10 @@ App.Task = App.AbstractEntity.extend({ endTime: DS.attr('number'), + duration: function () { + return App.Helpers.date.duration(this.get('startTime'), this.get('endTime')) + }.property('startTime', 'endTime'), + diagnostics: DS.attr('string'), numAttempts: DS.attr('number'), http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/router.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/router.js b/tez-ui/src/main/webapp/app/scripts/router.js index 50d47fd..6de5a5d 100644 --- a/tez-ui/src/main/webapp/app/scripts/router.js +++ b/tez-ui/src/main/webapp/app/scripts/router.js @@ -285,8 +285,19 @@ App.TezAppConfigsRoute = Em.Route.extend({ /* --- Shared routes --- */ -App.DagTasksRoute = - App.DagVerticesRoute = +App.DagTasksRoute = Em.Route.extend({ + renderTemplate: function () { + this.render('common/table'); + }, + setupController: function (controller, model) { + this._super(controller, model); + if(controller.loadData) { + controller.loadData(); + } + } +}); + +App.DagVerticesRoute = App.DagTaskAttemptsRoute = App.VertexTasksRoute = App.VertexTaskAttemptsRoute = http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/scripts/views/extra-table-buttons-view.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/scripts/views/extra-table-buttons-view.js b/tez-ui/src/main/webapp/app/scripts/views/extra-table-buttons-view.js new file mode 100644 index 0000000..422a755 --- /dev/null +++ b/tez-ui/src/main/webapp/app/scripts/views/extra-table-buttons-view.js @@ -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. + */ + +App.ExtraTableButtonsView = Ember.View.extend({ + templateName: 'views/extra-table-buttons', + + classNames: ['extra-table-buttons'] +}); http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/styles/colors.less ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/styles/colors.less b/tez-ui/src/main/webapp/app/styles/colors.less index 0fee7c0..9bf4b61 100644 --- a/tez-ui/src/main/webapp/app/styles/colors.less +++ b/tez-ui/src/main/webapp/app/styles/colors.less @@ -18,11 +18,16 @@ // Colors @logo-orange: #D27A22; + @bg-lite: #f5f5f5; @bg-liter: #f5f5f5; -@border-lite: #e5e5e5; @bg-red-light: #FFE6E6; +@bg-grey: #f0f0f0; + +@border-lite: #e5e5e5; +@border-color: #dcdcdc; + @white: #fff; @text-color: #666666; http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/styles/main.less ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/styles/main.less b/tez-ui/src/main/webapp/app/styles/main.less index c800ab2..870e2ec 100644 --- a/tez-ui/src/main/webapp/app/styles/main.less +++ b/tez-ui/src/main/webapp/app/styles/main.less @@ -30,6 +30,9 @@ body, html, body > .ember-view { height: 100%; overflow: visible; } +body, html { + min-width: 1024px; +} // Styles used in both standalone and wrapped modes .standalone, .wrapped { @@ -96,10 +99,15 @@ body, html, body > .ember-view { .standalone { height: 100%; - a { + a, .pagination > li > a, .btn-default, .clickable { color: @logo-orange; } + .is-current { + color: white; + background-color: @logo-orange; + } + #top-nav { a.logo { img { @@ -159,6 +167,11 @@ body, html, body > .ember-view { #top-nav, .footer, .push { display: none; } + + .is-current { + color: white; + background-color: @brand-primary; + } } .error-bar { @@ -519,6 +532,199 @@ div.indent { } .table-container { + .use-gpu; + + margin: 10px 0px; + + .table-header { + padding: 5px; + + button:hover { + background-color: @bg-grey; + } + + .table-message { + .inline-block; + font-size: 18px; + margin-top: 10px; + margin-left: 5px; + vertical-align: super; + + overflow: hidden; + transform: translateZ(0); + } + + .horizontal-half { + height: 33px; + white-space: nowrap; + } + + .extra-table-buttons { + .inline-block; + + .load-all { + .inline-block; + .left-divider; + + margin-left: 5px; + text-align: center; + + :first-child { + margin-bottom: -3px; + font-size: .85em; + color: red; + } + } + + .column-selector-button { + .fa-action; + .fa-icon(cog); + .left-divider; + .inline-block; + + padding-top: 5px; + height: 36px; + } + } + } + + .waiting { + .fa; + .fa-icon(spinner); + .fa-spin; + .fa-fw; + } + + .pagination-view { + .inline-block; + + .page-list { + .inline-block; + .align-top; + + border: 1px solid @border-color; + border-radius: 5px; + + padding: 0px; + + font-size: 0px; + + li { + .inline-block; + + padding: 6px 10px; + font-size: 14px; + + border-left: 1px solid @border-color; + + pointer-events: none; + + &.clickable { + pointer-events: auto; + &:hover { + background-color: @bg-grey; + cursor: pointer; + } + } + } + + .total-page-count { + font-size: .8em; + } + + :first-child { + border-left: none; + } + } + + .row-select { + margin-left: 5px; + + display: inline-block; + text-align: center; + + :first-child { + margin-bottom: -3px; + font-size: .85em; + } + } + } + + .table-body-container{ + border: 1px solid @border-color; + border-radius: 5px; + + margin: 5px 0px; + + overflow: auto; + + .table-body { + .noise-background; + white-space: nowrap; + font-size: 0; + + .table-column { + background-color: white; + display: inline-block; + min-width: 150px; + border-left: 1px solid @border-color; + + &.first-item { + border-left: none; + } + + .table-header-cell, .table-cell { + font-size: 13px; + padding: 5px; + position: relative; + + .cell-content { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + height: 18px; + } + + .resize-column { + .fa; + .fa-icon(ellipsis-v); + + cursor: col-resize; + position: absolute; + right: 2px; + top: 10px; + + opacity: .2; + } + } + + .table-header-cell { + font-weight: bold; + + padding-right: 15px; + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + background-color: @bg-grey; + border-bottom: 1px solid @border-color; + } + + .table-cell { + border-top: 1px dotted @border-color; + + &.first-item { + border-top: none; + } + } + } + } + } +} + +.ember-table-container { min-height: 20px; width: 100%; overflow: auto; @@ -532,6 +738,23 @@ div.indent { } } +.filter-cell, { + left: 0px; + top: 0px; + .sort, .filter { + position: absolute; + } + .filter { + left: 5px; + right: 20px; + } + .sort { + cursor: pointer; + top: 7px; + right: 5px; + } +} + .input-dirty { background-color: yellow; } http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/styles/shared.less ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/styles/shared.less b/tez-ui/src/main/webapp/app/styles/shared.less index 617c87f..cebd837 100644 --- a/tez-ui/src/main/webapp/app/styles/shared.less +++ b/tez-ui/src/main/webapp/app/styles/shared.less @@ -43,4 +43,28 @@ .no-wrap { white-space: nowrap; +} + +.align-top{ + vertical-align: top; +} + +.inline-block { + display: inline-block; +} + +.noise-background { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoY RxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4 wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==); +} + +.absolute { + position: absolute; +} + +.use-gpu { + -webkit-transform: translateZ(0); + -moz-transform: translateZ(0); + -ms-transform: translateZ(0); + -o-transform: translateZ(0); + transform: translateZ(0); } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/common/table.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/common/table.hbs b/tez-ui/src/main/webapp/app/templates/common/table.hbs new file mode 100644 index 0000000..2c54903 --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/common/table.hbs @@ -0,0 +1,47 @@ +{{! +* 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. +}} + +{{#unless loading}} + {{#if data.length}} + {{load-time-component + isRefreshable=isRefreshable + time=data.content.0.timeStamp + refresh='refresh' + }} + + {{basic-table-component + columns=columns + rows=data.content + + extraHeaderItem=App.ExtraTableButtonsView + statusMessage=statusMessage + + enablePagination=true + + pageNumBinding='pageNum' + rowCountBinding='rowCount' + }} + {{else}} + <h1>No records available!</n1> + {{/if}} +{{else}} + {{partial 'partials/loading-spinner'}} + <div class="text-align-center"> + {{statusMessage}} + </div> +{{/unless}} http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/components/basic-table.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/components/basic-table.hbs b/tez-ui/src/main/webapp/app/templates/components/basic-table.hbs new file mode 100644 index 0000000..ac4a466 --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/components/basic-table.hbs @@ -0,0 +1,58 @@ +{{! +* 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. +}} + +<div class='table-container'> + {{#if _showHeader}} + <div class='table-header'> + <div class="horizontal-half align-top"> + </div><div class="horizontal-half align-top align-children-right"> + {{#if enablePagination}} + {{view App.BasicTableComponent.PaginationView pageNum=pageNum totalPages=totalPages}} + {{/if}} + {{#if extraHeaderItem}} + {{view extraHeaderItem}} + {{/if}} + </div> + </div> + {{/if}} + <div class='table-body-container'> + <div class='table-body'> + {{#each column in _columns}} + <div {{bind-attr + style=column.customStyle + class=":table-column _view.contentIndex::first-item" + }}> + {{view column.headerCellView}} + {{#each row in _rows}} + <div class='table-cell {{unbound firstItemCSS _view}}'> + {{view column.cellView row=row}} + </div> + {{/each}} + </div> + {{/each}} + </div> + </div> + <div class='table-footer'> + <div class="horizontal-half align-top"> + </div><div class="horizontal-half align-top align-children-right"> + {{#if hasPageNavOnFooter}} + {{view App.BasicTableComponent.PaginationView pageNum=pageNum totalPages=totalPages}} + {{/if}} + </div> + </div> +</div> http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/components/basic-table/basic-cell.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/components/basic-table/basic-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/basic-table/basic-cell.hbs new file mode 100644 index 0000000..370637b --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/components/basic-table/basic-cell.hbs @@ -0,0 +1,19 @@ +{{! +* 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. +}} + +{{unbound view.cellContent}} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/components/basic-table/header-cell.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/components/basic-table/header-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/basic-table/header-cell.hbs new file mode 100644 index 0000000..1159524 --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/components/basic-table/header-cell.hbs @@ -0,0 +1,24 @@ +{{! +* 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. +}} + +<div class='table-header-cell'> + <div class='cell-content'> + {{unbound column.headerCellName}} + </div> + <i class="resize-column" {{action 'startColResize' on=mouseDown target='view'}} ></i> +</div> http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/components/basic-table/linked-cell.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/components/basic-table/linked-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/basic-table/linked-cell.hbs new file mode 100644 index 0000000..3297ad7 --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/components/basic-table/linked-cell.hbs @@ -0,0 +1,25 @@ +{{! +* 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 view.cellContent._notAvailable}} + {{view.cellContent}} +{{else}} + {{#link-to view.cellContent.linkTo view.cellContent.entityId}} + {{view.cellContent.displayText}} + {{/link-to}} +{{/if}} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/components/basic-table/logs-cell.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/components/basic-table/logs-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/basic-table/logs-cell.hbs new file mode 100644 index 0000000..092b6ee --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/components/basic-table/logs-cell.hbs @@ -0,0 +1,35 @@ +{{! +* 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. +}} + +<span class="ember-table-content"> + {{#if view.cellContent.isPending}} + <i class="waiting absolute"></i> + {{else}} + {{#if view.cellContent._notAvailable}} + {{view.cellContent}} + {{else}} + {{#if view.cellContent.viewUrl}} + <a target="_blank" href="//{{unbound view.cellContent.viewUrl}}">View</a> + + {{/if}} + {{#if view.cellContent.downloadUrl}} + <a target="_blank" href="{{unbound view.cellContent.downloadUrl}}?start=0" download type="application/octet-stream">Download</a> + {{/if}} + {{/if}} + {{/if}} +</span> http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/components/basic-table/pagination-view.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/components/basic-table/pagination-view.hbs b/tez-ui/src/main/webapp/app/templates/components/basic-table/pagination-view.hbs new file mode 100644 index 0000000..808fe9d --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/components/basic-table/pagination-view.hbs @@ -0,0 +1,38 @@ +{{! +* 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. +}} + +<ul class="page-list"> + <li {{bind-attr class="view.atFirst::clickable"}} {{action 'changePage' 1}}> + First page + </li> + {{#each page in view._possiblePages}} + <li {{bind-attr class="page.isCurrent:is-current:clickable"}} {{action 'changePage' page.pageNum}}> + {{page.pageNum}} + </li> + {{/each}} + <li {{bind-attr class="view.atLast::clickable"}} {{action 'changePage' view.totalPages}}> + Last page - {{view.totalPages}} + </li> +</ul> +<div class='row-select'> + <div>Rows</div> + {{view Ember.Select + content=rowCountOptions + value=rowCount + }} +</div> http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/components/basic-table/status-cell.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/components/basic-table/status-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/basic-table/status-cell.hbs new file mode 100644 index 0000000..a7caa35 --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/components/basic-table/status-cell.hbs @@ -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. +}} + +{{#if view.cellContent._notAvailable}} + {{view.cellContent}} +{{else}} + <span class="ember-table-content"> + <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i> + {{view.cellContent.status}} + {{#if view.cellContent.progress}} + {{bs-badge content=view.cellContent.progress}} + {{/if}} + </span> +{{/if}} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/components/basic-table/task-actions-cell.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/components/basic-table/task-actions-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/basic-table/task-actions-cell.hbs new file mode 100644 index 0000000..b914b82 --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/components/basic-table/task-actions-cell.hbs @@ -0,0 +1,26 @@ +{{! +* 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 view.cellContent._notAvailable}} + {{view.cellContent}} +{{else}} + <span class="ember-table-content"> + {{#link-to "task.counters" view.cellContent}}counters{{/link-to}} + {{#link-to "task.attempts" view.cellContent}}attempts{{/link-to}} + </span> +{{/if}} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tez/blob/9316fe62/tez-ui/src/main/webapp/app/templates/views/extra-table-buttons.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/views/extra-table-buttons.hbs b/tez-ui/src/main/webapp/app/templates/views/extra-table-buttons.hbs new file mode 100644 index 0000000..98099db --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/views/extra-table-buttons.hbs @@ -0,0 +1,19 @@ +{{! + * 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. +}} + +<i class='column-selector-button' {{action 'selectColumns' target=targetObject}}></i>
