Przeglądaj źródła

AMBARI-6476. Create models for jobs. (onechiporenko)

Oleg Nechiporenko 11 lat temu
rodzic
commit
64a65149f4

+ 4 - 1
contrib/views/jobs/src/main/resources/ui/app/scripts/app.js

@@ -18,10 +18,13 @@
 
 var App = window.App = Ember.Application.create();
 
+App.Helpers = Ember.Namespace.create();
+
 /* Order and include as you please. */
+require('scripts/helpers/*');
 require('scripts/controllers/*');
 require('scripts/store');
-require('scripts/models/*');
+require('scripts/models/**/*');
 require('scripts/routes/*');
 require('scripts/components/*');
 require('scripts/views/*');

+ 216 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/date.js

@@ -0,0 +1,216 @@
+/**
+ * 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.date = {
+
+  /**
+   * List of monthes short names
+   * @type {string[]}
+   */
+  dateMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+
+  /**
+   * List of days short names
+   * @type {string[]}
+   */
+  dateDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+
+  /**
+   * Add leading zero
+   *
+   * @param {string} time
+   * @returns {string}
+   * @method dateFormatZeroFirst
+   */
+  dateFormatZeroFirst: function (time) {
+    if (time < 10) return '0' + time;
+    return ""  + time;
+  },
+
+  /**
+   * Convert timestamp to date-string 'DAY_OF_THE_WEEK, MONTH DAY, YEAR HOURS:MINUTES'
+   *
+   * @param {number} timestamp
+   * @param {bool} showSeconds should seconds be added to result string
+   * @param {bool} showMilliseconds should miliseconds be added to result string (if <code>showSeconds</code> is false, milliseconds wouldn't be added)
+   * @return {*} date
+   * @method dateFormat
+   */
+  dateFormat: function (timestamp, showSeconds, showMilliseconds) {
+    if (!App.Helpers.validator.isValidInt(timestamp)) {
+      return timestamp;
+    }
+    var format = 'ddd, MMM DD, YYYY HH:mm';
+    if (showSeconds) {
+      format += ':ss';
+      if (showMilliseconds) {
+        format += ':SSS';
+      }
+    }
+    return moment((new Date(timestamp)).toISOString().replace('Z', '')).format(format);
+  },
+
+  /**
+   * Convert timestamp to date-string 'DAY_OF_THE_WEEK MONTH DAY YEAR'
+   *
+   * @param {string} timestamp
+   * @return {string}
+   * @method dateFormatShort
+   */
+  dateFormatShort: function (timestamp) {
+    if (!App.Helpers.validator.isValidInt(timestamp)) {
+      return timestamp;
+    }
+    var format = 'ddd MMM DD YYYY';
+    var date = moment((new Date(timestamp)).toISOString().replace('Z', '')).format(format);
+    var today = moment((new Date()).toISOString().replace('Z', '')).format(format);
+    if (date === today) {
+      return 'Today ' + (new Date(timestamp)).toLocaleTimeString();
+    }
+    return date;
+  },
+
+  /**
+   * Convert starTimestamp to 'DAY_OF_THE_WEEK, MONTH DAY, YEAR HOURS:MINUTES', except for the case: year equals 1969
+   *
+   * @param {string} startTimestamp
+   * @return {string} startTimeSummary
+   * @method startTime
+   */
+  startTime: function (startTimestamp) {
+    if (!App.Helpers.validator.isValidInt(startTimestamp)) {
+      return '';
+    }
+    var startDate = new Date(startTimestamp);
+    var months = this.dateMonths;
+    var days = this.dateDays;
+    // generate start time
+    if (startDate.getFullYear() == 1969 || startTimestamp < 1) {
+      return 'Not started';
+    }
+    var startTimeSummary = '';
+    if (new Date(startTimestamp).setHours(0, 0, 0, 0) == new Date().setHours(0, 0, 0, 0)) { //today
+      startTimeSummary = 'Today ' + this.dateFormatZeroFirst(startDate.getHours()) + ':' + this.dateFormatZeroFirst(startDate.getMinutes());
+    } else {
+      startTimeSummary = days[startDate.getDay()] + ' ' + months[startDate.getMonth()] + ' ' +
+        this.dateFormatZeroFirst(startDate.getDate()) + ' ' + startDate.getFullYear() + ' '
+        + this.dateFormatZeroFirst(startDate.getHours()) + ':' + this.dateFormatZeroFirst(startDate.getMinutes());
+    }
+    return startTimeSummary;
+  },
+
+  /**
+   * Provides the duration between the given start and end timestamp. If start time
+   * not valid, duration will be ''. If end time is not valid, duration will
+   * be till now, showing 'Lasted for xxx secs'.
+   *
+   * @param {string} startTimestamp
+   * @param {string} endTimestamp
+   * @return {string} durationSummary
+   * @method durationSummary
+   */
+  durationSummary: function (startTimestamp, endTimestamp) {
+    // generate duration
+    var durationSummary = '';
+    var startDate = new Date(startTimestamp);
+    var endDate = new Date(endTimestamp);
+    var self = this;
+    if (startDate.getFullYear() == 1969 || startTimestamp < 1) {
+      // not started
+      return Em.I18n.t('common.na');
+    }
+    if (endDate.getFullYear() != 1969 && endTimestamp > 0) {
+      return '' + this.timingFormat(endTimestamp - startTimestamp, 1); //lasted for xx secs
+    } else {
+      // still running, duration till now
+      var time = (App.dateTime() - startTimestamp) < 0 ? 0 : (App.dateTime() - startTimestamp);
+      durationSummary = '' + this.timingFormat(time, 1);
+    }
+    return durationSummary;
+  },
+
+  /**
+   * Convert time in mseconds to
+   * 30 ms = 30 ms
+   * 300 ms = 300 ms
+   * 999 ms = 999 ms
+   * 1000 ms = 1.00 secs
+   * 3000 ms = 3.00 secs
+   * 35000 ms = 35.00 secs
+   * 350000 ms = 350.00 secs
+   * 999999 ms = 999.99 secs
+   * 1000000 ms = 16.66 mins
+   * 3500000 secs = 58.33 mins
+   *
+   * @param {number} time
+   * @param {bool} zeroValid for the case to show 0 when time is 0, not null
+   * @return {string|null} formatted date
+   * @method timingFormat
+   */
+  timingFormat: function (time, /* optional */ zeroValid) {
+    var intTime = parseInt(time);
+    if (zeroValid && intTime == 0) {
+      return 0 + ' secs';
+    }
+    if (!intTime) {
+      return null;
+    }
+    var timeStr = intTime.toString();
+    var lengthOfNumber = timeStr.length;
+    var oneMinMs = 60000;
+    var oneHourMs = 3600000;
+    var oneDayMs = 86400000;
+
+    if (lengthOfNumber < 4) {
+      return time + ' ms';
+    } else if (lengthOfNumber < 7) {
+      time = (time / 1000).toFixed(2);
+      return time + ' secs';
+    } else if (time < oneHourMs) {
+      time = (time / oneMinMs).toFixed(2);
+      return time + ' mins';
+    } else if (time < oneDayMs) {
+      time = (time / oneHourMs).toFixed(2);
+      return time + ' hours';
+    } else {
+      time = (time / oneDayMs).toFixed(2);
+      return time + ' days';
+    }
+  },
+
+  /**
+   * Provides the duration between the given start and end time. If start time
+   * is not given, duration will be 0. If end time is not given, duration will
+   * be till now.
+   *
+   * @param {Number} startTime Start time from epoch
+   * @param {Number} endTime End time from epoch
+   * @return {Number} duration
+   * @method duration
+   */
+  duration: function (startTime, endTime) {
+    var duration = 0;
+    if (startTime && startTime > 0) {
+      if (!endTime || endTime < 1) {
+        endTime = App.dateTime();
+      }
+      duration = endTime - startTime;
+    }
+    return duration;
+  }
+};

+ 41 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/misc.js

@@ -0,0 +1,41 @@
+/**
+ * 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 = {
+
+  /**
+   * Convert value from bytes to appropriate measure
+   */
+  formatBandwidth: function (value) {
+    if (value) {
+      if (value < 1024) {
+        value = '<1KB';
+      } else {
+        if (value < 1048576) {
+          value = (value / 1024).toFixed(1) + 'KB';
+        } else  if (value >= 1048576 && value < 1073741824){
+          value = (value / 1048576).toFixed(1) + 'MB';
+        } else {
+          value = (value / 1073741824).toFixed(2) + 'GB';
+        }
+      }
+    }
+    return value;
+  }
+
+};

+ 57 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/number.js

@@ -0,0 +1,57 @@
+/**
+ * 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.number = {
+
+  /**
+   * Convert byte size to other metrics.
+   * 
+   * @param {Number} bytes to convert to string
+   * @param {Number} precision Number to adjust precision of return value. Default is 0.
+   * @param {String} parseType
+   *           JS method name for parse string to number. Default is "parseInt".
+   * @param {Number} multiplyBy bytes by this number if given. This is needed
+   *          as <code>null * 1024 = 0</null>
+   * @remarks The parseType argument can be "parseInt" or "parseFloat".
+   * @return {String} Returns converted value with abbreviation.
+   */
+  bytesToSize: function (bytes, precision, parseType, multiplyBy) {
+    if (bytes === null || bytes === undefined) {
+      return 'n/a';
+    } else {
+      if (arguments[2] === undefined) {
+        parseType = 'parseInt';
+      }
+      if (arguments[3] === undefined) {
+        multiplyBy = 1;
+      }
+      var value = bytes * multiplyBy;
+      var sizes = [ 'Bytes', 'KB', 'MB', 'GB', 'TB', 'PB' ];
+      var posttxt = 0;
+      while (value >= 1024) {
+        posttxt++;
+        value = value / 1024;
+      }
+      if (value === 0) {
+        precision = 0;
+      }
+      var parsedValue = window[parseType](value);
+      return parsedValue.toFixed(precision) + " " + sizes[posttxt];
+    }
+  }
+
+};

+ 45 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/string.js

@@ -0,0 +1,45 @@
+/**
+ * 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.string = {
+
+  /**
+   * Determines whether string end within another string.
+   *
+   * @method endsWith
+   * @param {string} str string
+   * @param {string} suffix substring for search
+   * @return {boolean}
+   */
+  endsWith: function (str, suffix) {
+    return str.indexOf(suffix, this.length - suffix.length) !== -1;
+  },
+
+  /**
+   * Determines whether string start within another string.
+   *
+   * @method startsWith
+   * @param {string} str string
+   * @param {string} prefix substring for search
+   * @return {boolean}
+   */
+  startsWith: function (str, prefix) {
+    return str.indexOf(prefix) == 0;
+  }
+
+};

+ 26 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/validator.js

@@ -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.
+ */
+
+App.Helpers.validator = {
+
+  isValidInt: function(value) {
+    var intRegex = /^-?\d+$/;
+    return intRegex.test(value);
+  }
+
+};

+ 69 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/models/job.js

@@ -0,0 +1,69 @@
+/**
+ * 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.Job = DS.Model.extend({
+
+  run: DS.belongsTo('App.Run'),
+
+  jobName: DS.attr('string'),
+
+  workflowEntityName: DS.attr('string'),
+
+  userName: DS.attr('string'),
+
+  confPath: DS.attr('string'),
+
+  submitTime: DS.attr('number'),
+
+  maps: DS.attr('number'),
+
+  reduces: DS.attr('number'),
+
+  status: DS.attr('string'),
+
+  input: DS.attr('number'),
+
+  output: DS.attr('number'),
+
+  elapsedTime: DS.attr('number'),
+
+  duration: function () {
+    return App.Helpers.date.timingFormat(parseInt(this.get('elapsedTime')));
+  }.property('elapsedTime'),
+
+  jobTimeLine: DS.attr('string'),
+
+  jobTaskView: DS.attr('string'),
+
+  /**
+   *  Sum of input bandwidth for all jobs with appropriate measure
+   */
+  inputFormatted: function () {
+    return App.Helpers.misc.formatBandwidth(this.get('input'));
+  }.property('input'),
+
+  /**
+   *  Sum of output bandwidth for all jobs with appropriate measure
+   */
+  outputFormatted: function () {
+    return App.Helpers.misc.formatBandwidth(this.get('output'));
+  }.property('output')
+
+});
+
+App.Job.FIXTURES = [];

+ 74 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/models/jobs/abstract_job.js

@@ -0,0 +1,74 @@
+/**
+ * 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.
+ */
+
+/**
+ * Base class of all Jobs.
+ *
+ * This class is meant to be extended and not instantiated directly.
+ */
+App.AbstractJob = DS.Model.extend({
+
+  id : DS.attr('string'),
+
+  name : DS.attr('string'),
+
+  user : DS.attr('string'),
+
+  startTime : DS.attr('number'),
+
+  endTime : DS.attr('number'),
+
+  startTimeDisplay : function() {
+    var startTime = this.get('startTime');
+    return startTime > 0 ? App.Helpers.date.dateFormat(startTime) : '';
+  }.property('startTime'),
+
+  endTimeDisplay : function() {
+    var endTime = this.get('endTime');
+    return endTime > 0 ? App.Helpers.date.dateFormat(endTime) : '';
+  }.property('endTime'),
+
+  /**
+   * Provides the duration of this job. If the job has not started, duration
+   * will be given as 0. If the job has not ended, duration will be till now.
+   *
+   * @return {Number} Duration in milliseconds.
+   */
+  duration : function() {
+    var startTime = this.get('startTime');
+    var endTime = this.get('endTime');
+    if(endTime < startTime || endTime == undefined) {
+      endTime =  App.dateTime();
+    }
+    return dateUtils.duration(startTime, endTime);
+  }.property('startTime', 'endTime'),
+
+  durationDisplay : function() {
+    return App.Helpers.date.timingFormat(this.get('duration'), true);
+  }.property('duration'),
+
+  /**
+   * Type of this job. Should be one of constants defined in App.JobType
+   */
+  jobType : DS.attr('string')
+});
+
+App.JobType = {
+  HIVE : "hive"
+};
+
+App.AbstractJob.FIXTURES = [];

+ 34 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/models/jobs/hive_job.js

@@ -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.
+ */
+
+App.HiveJob = App.AbstractJob.extend({
+
+  jobType : App.JobType.HIVE,
+
+  queryText : DS.attr('string'),
+
+  stages : DS.attr('array'),
+
+  hasTezDag: DS.attr('boolean'),
+
+  tezDag : DS.belongsTo('App.TezDag'),
+
+  failed: DS.attr('boolean')
+
+});
+
+App.HiveJob.FIXTURES = [];

+ 209 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/models/jobs/tez_dag.js

@@ -0,0 +1,209 @@
+/**
+ * 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.TezDag = DS.Model.extend({
+
+  id : DS.attr('string'),
+
+  /**
+   * When DAG is actually running on server, a unique ID is assigned.
+   */
+  instanceId : DS.attr('string'),
+
+  name : DS.attr('string'),
+
+  yarnApplicationId: DS.attr('string'),
+
+  stage : DS.attr('string'),
+
+  vertices : DS.hasMany('App.TezDagVertex'),
+
+  edges : DS.hasMany('App.TezDagEdge')
+
+});
+
+App.TezDagEdge = DS.Model.extend({
+
+  id : DS.attr('string'),
+
+  instanceId : DS.attr('string'),
+
+  fromVertex : DS.belongsTo('App.TezDagVertex'),
+
+  toVertex : DS.belongsTo('App.TezDagVertex'),
+
+  /**
+   * Type of this edge connecting vertices. Should be one of constants defined
+   * in 'App.TezDagEdgeType'.
+   */
+  edgeType : DS.attr('string')
+});
+
+App.TezDagVertex = DS.Model.extend({
+
+  id : DS.attr('string'),
+  /**
+   * When DAG vertex is actually running on server, a unique ID is assigned.
+   */
+  instanceId : DS.attr('string'),
+
+  name : DS.attr('string'),
+
+  /**
+   * State of this vertex. Should be one of constants defined in
+   * App.TezDagVertexState.
+   */
+  state : DS.attr('string'),
+
+  /**
+   * Vertex type has to be one of the types defined in 'App.TezDagVertexType'
+   * @return {string}
+   */
+  type : DS.attr('string'),
+
+  /**
+   * A vertex can have multiple incoming edges.
+   */
+  incomingEdges : DS.hasMany('App.TezDagEdge'),
+
+  /**
+   * This vertex can have multiple outgoing edges.
+   */
+  outgoingEdges : DS.hasMany('App.TezDagEdge'),
+
+  startTime : DS.attr('number'),
+
+  endTime : DS.attr('number'),
+
+  /**
+   * Provides the duration of this job. If the job has not started, duration
+   * will be given as 0. If the job has not ended, duration will be till now.
+   *
+   * @return {Number} Duration in milliseconds.
+   */
+  duration : function() {
+    return App.Helpers.date.duration(this.get('startTime'), this.get('endTime'))
+  }.property('startTime', 'endTime'),
+
+  /**
+   * Each Tez vertex can perform arbitrary application specific computations
+   * inside. The application can provide a list of operations it has provided in
+   * this vertex.
+   *
+   * Array of strings. [{string}]
+   */
+  operations : DS.attr('array'),
+
+  /**
+   * Provides additional information about the 'operations' performed in this
+   * vertex. This is shown directly to the user.
+   */
+  operationPlan : DS.attr('string'),
+
+  /**
+   * Number of actual Map/Reduce tasks in this vertex
+   */
+  tasksCount : DS.attr('number'),
+
+  tasksNumber: function () {
+    return this.get('tasksCount') ? this.get('tasksCount') : 0;
+  }.property('tasksCount'),
+
+  /**
+   * Local filesystem usage metrics for this vertex
+   */
+  fileReadBytes : DS.attr('number'),
+
+  fileWriteBytes : DS.attr('number'),
+
+  fileReadOps : DS.attr('number'),
+
+  fileWriteOps : DS.attr('number'),
+
+  /**
+   * Spilled records
+   */
+  spilledRecords : DS.attr('number'),
+
+  /**
+   * HDFS usage metrics for this vertex
+   */
+  hdfsReadBytes : DS.attr('number'),
+
+  hdfsWriteBytes : DS.attr('number'),
+
+  hdfsReadOps : DS.attr('number'),
+
+  hdfsWriteOps : DS.attr('number'),
+
+  /**
+   * Record metrics for this vertex
+   */
+  recordReadCount : DS.attr('number'),
+
+  recordWriteCount : DS.attr('number'),
+
+  totalReadBytes : function() {
+    return this.get('fileReadBytes') + this.get('hdfsReadBytes');
+  }.property('fileReadBytes', 'hdfsReadBytes'),
+
+  totalWriteBytes : function() {
+    return this.get('fileWriteBytes') + this.get('hdfsWriteBytes');
+  }.property('fileWriteBytes', 'hdfsWriteBytes'),
+
+  totalReadBytesDisplay : function() {
+    return  App.Helpers.number.bytesToSize(this.get('totalReadBytes'));
+  }.property('totalReadBytes'),
+
+  totalWriteBytesDisplay : function() {
+    return  App.Helpers.number.bytesToSize(this.get('totalWriteBytes'));
+  }.property('totalWriteBytes'),
+
+  durationDisplay : function() {
+    return App.Helpers.date.timingFormat(this.get('duration'), true);
+  }.property('duration')
+
+});
+
+App.TezDagVertexState = {
+  NEW : "NEW",
+  INITIALIZING : "INITIALIZING",
+  INITED : "INITED",
+  RUNNING : "RUNNING",
+  SUCCEEDED : "SUCCEEDED",
+  FAILED : "FAILED",
+  KILLED : "KILLED",
+  ERROR : "ERROR",
+  TERMINATING : "TERMINATING",
+  JOBFAILED: "JOB FAILED"
+};
+
+App.TezDagVertexType = {
+  MAP: 'MAP',
+  REDUCE: 'REDUCE',
+  UNION: 'UNION'
+};
+
+App.TezDagEdgeType = {
+  SCATTER_GATHER : "SCATTER_GATHER",
+  BROADCAST : "BROADCAST",
+  CONTAINS: "CONTAINS"
+};
+
+App.TezDag.FIXTURES = [];
+App.TezDagEdge.FIXTURES = [];
+App.TezDagVertex.FIXTURES = [];

+ 121 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/models/run.js

@@ -0,0 +1,121 @@
+/**
+ * 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.Run = DS.Model.extend({
+
+  appName: DS.attr('string'),
+
+  userName:DS.attr('string'),
+
+  numJobsTotal: DS.attr('number'),
+
+  numJobsCompleted: DS.attr('number'),
+
+  startTime:DS.attr('string'),
+
+  elapsedTime:DS.attr('string'),
+
+  workflowContext:DS.attr('string'),
+
+  input: DS.attr('number'),
+
+  output: DS.attr('number'),
+
+  loadAllJobs : false,
+
+  isStared: false,
+
+  isFiltered: false,
+
+  /**
+   * runId  short part
+   */
+  idFormatted: function() {
+    return this.get('id').substr(0, 20);
+  }.property('id'),
+
+  /**
+   * Jobs in the current run
+   */
+  jobs: function() {
+    return App.Job.find().filterProperty('run.id', this.get('id'));
+  }.property('loadAllJobs'),
+
+  /**
+   * Run duration
+   */
+  duration: function() {
+    return App.Helpers.date.timingFormat(parseInt(this.get('elapsedTime')));
+  }.property('elapsedTime'),
+
+  /**
+   * Status of running jobs
+   */
+  isRunning: function () {
+    return !this.get('numJobsTotal') == this.get('numJobsCompleted');
+  }.property('numJobsTotal', 'numJobsCompleted'),
+
+  /**
+   * Sum of input bandwidth for all jobs with appropriate measure
+   */
+  inputFormatted: function () {
+    var input = this.get('input');
+    input = App.Helpers.misc.formatBandwidth(input);
+    return input;
+  }.property('input'),
+
+  /**
+   *  Sum of output bandwidth for all jobs with appropriate measure
+   */
+  outputFormatted: function () {
+    var output = this.get('output');
+    output = App.Helpers.misc.formatBandwidth(output);
+    return output;
+  }.property('output'),
+
+  /**
+   *
+   */
+  lastUpdateTime: function() {
+    return parseInt(this.get('startTime')) + parseInt(this.get('elapsedTime'));
+  }.property('elapsedTime', 'startTime'),
+
+  /**
+   *
+   */
+  lastUpdateTimeFormatted: function() {
+    return App.Helpers.date.dateFormat(this.get('lastUpdateTime'));
+  }.property('lastUpdateTime'),
+
+  lastUpdateTimeFormattedShort: function(){
+    return App.Helpers.date.dateFormatShort(this.get('lastUpdateTime'));
+  }.property('lastUpdateTime'),
+
+  /**
+   * Type value based on first part of id
+   */
+  type: function() {
+    var id = this.get('id');
+    if(App.Helpers.string.startsWith(id, 'pig_')) return 'Pig';
+    if(App.Helpers.string.startsWith(id, 'hive_')) return 'Hive';
+    if(App.Helpers.string.startsWith(id, 'mr_')) return 'MapReduce';
+    return '';
+  }.property('id')
+});
+
+App.Run.FIXTURES = [];