You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ma...@apache.org on 2013/02/04 03:24:02 UTC
svn commit: r1442010 [29/29] - in /incubator/ambari/branches/branch-1.2: ./
ambari-agent/ ambari-agent/conf/unix/ ambari-agent/src/examples/
ambari-agent/src/main/puppet/modules/hdp-ganglia/files/
ambari-agent/src/main/puppet/modules/hdp-ganglia/manife...
Modified: incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step2_view.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step2_view.js?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step2_view.js (original)
+++ incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step2_view.js Mon Feb 4 02:23:55 2013
@@ -20,7 +20,8 @@
var App = require('app');
App.SshKeyFileUploader = Ember.View.extend({
- template:Ember.Handlebars.compile('<input type="file" />'),
+ template:Ember.Handlebars.compile('<input type="file" {{bindAttr disabled="view.disabled"}} />'),
+ classNames: ['ssh-key-input-indentation'],
change: function (e) {
var self=this;
@@ -61,62 +62,9 @@ App.WizardStep2View = Em.View.extend({
this.set('controller.sshKeyError',null);
this.loadHostsInfo();
},
- /**
- * Config for displaying more hosts
- * if oldHosts.length more than config.count that configuration will be applied
- */
- hostDisplayConfig: [
- {
- count: 0,
- delimitery: '<br/>',
- popupDelimitery: '<br />'
- },
- {
- count: 10,
- delimitery: ', ',
- popupDelimitery: '<br />'
- },
- {
- count: 50,
- delimitery: ', ',
- popupDelimitery: ', '
- }
- ],
- showMoreHosts: function () {
- var self = this;
- App.ModalPopup.show({
- header: "Hosts are already part of the cluster and will be ignored",
- body: self.get('hostsInfo.oldHostNamesMore'),
- encodeBody: false,
- onPrimary: function () {
- this.hide();
- },
- secondary: null
- });
- },
- loadHostsInfo: function(){
+ loadHostsInfo: function(){
var hostsInfo = Em.Object.create();
-
- var oldHostNames = App.Host.find().getEach('id');
- var k = 10;
-
- var usedConfig = false;
- this.get('hostDisplayConfig').forEach(function (config) {
- if (oldHostNames.length > config.count) {
- usedConfig = config;
- }
- });
-
- k = usedConfig.count ? usedConfig.count : oldHostNames.length;
- var displayedHostNames = oldHostNames.slice(0, k);
- hostsInfo.set('oldHostNames', displayedHostNames.join(usedConfig.delimitery));
- if (usedConfig.count) {
- var moreHostNames = oldHostNames.slice(k + 1);
- hostsInfo.set('oldHostNamesMore', moreHostNames.join(usedConfig.popupDelimitery));
- hostsInfo.set('showMoreHostsText', "...and %@ more".fmt(moreHostNames.length));
- }
-
this.set('hostsInfo', hostsInfo);
},
@@ -132,26 +80,10 @@ App.WizardStep2View = Em.View.extend({
return this.get("controller.content.installOptions.manualInstall");
}.property("controller.content.installOptions.manualInstall"),
- sshKeyClass:function() {
- return (this.get("isFileApi")) ? "hide" : "" ;
- }.property("isFileApi"),
-
isFileApi: function () {
return (window.File && window.FileReader && window.FileList) ? true : false ;
}.property(),
- sshKeyPreviewClass: function() {
- if (this.get('controller.content.installOptions.sshKey').trim() != '') {
- if (this.get('controller.content.installOptions.manualInstall')) {
- return 'sshKey-file-view disabled';
- } else {
- return 'sshKey-file-view';
- }
- } else {
- return 'hidden';
- }
- }.property('controller.content.installOptions.sshKey', 'controller.content.installOptions.manualInstall'),
-
manualInstallPopup: function(){
if(!this.get('controller.content.installOptions.useSsh')){
App.ModalPopup.show({
@@ -165,8 +97,35 @@ App.WizardStep2View = Em.View.extend({
});
}
this.set('controller.content.installOptions.manualInstall', !this.get('controller.content.installOptions.useSsh'));
- }.observes('controller.content.installOptions.useSsh')
+ }.observes('controller.content.installOptions.useSsh'),
+ providingSSHKeyRadioButton: Ember.Checkbox.extend({
+ tagName: 'input',
+ attributeBindings: ['type', 'checked'],
+ checked: function () {
+ return this.get('controller.content.installOptions.useSsh');
+ }.property('controller.content.installOptions.useSsh'),
+ type: 'radio',
+
+ click: function () {
+ this.set('controller.content.installOptions.useSsh', true);
+ this.set('controller.content.installOptions.manualInstall', false);
+ }
+ }),
+
+ manualRegistrationRadioButton: Ember.Checkbox.extend({
+ tagName: 'input',
+ attributeBindings: ['type', 'checked'],
+ checked: function () {
+ return this.get('controller.content.installOptions.manualInstall');
+ }.property('controller.content.installOptions.manualInstall'),
+ type: 'radio',
+
+ click: function () {
+ this.set('controller.content.installOptions.manualInstall', true);
+ this.set('controller.content.installOptions.useSsh', false);
+ }
+ })
});
Modified: incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step8_view.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step8_view.js?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step8_view.js (original)
+++ incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step8_view.js Mon Feb 4 02:23:55 2013
@@ -27,6 +27,7 @@ App.WizardStep8View = Em.View.extend({
var controller = this.get('controller');
controller.loadStep();
},
+
spinner : null,
printReview: function() {
@@ -34,49 +35,60 @@ App.WizardStep8View = Em.View.extend({
o.jqprint();
},
- showLoadingIndicator: function(){
- if(this.get('controller.hasErrorOccurred')){
- $('.spinner').hide();
+ ajaxQueueLength: function() {
+ return this.get('controller.ajaxQueueLength');
+ }.property('controller.ajaxQueueLength'),
+
+ ajaxQueueLeft: function() {
+ return this.get('controller.ajaxQueueLeft');
+ }.property('controller.ajaxQueueLeft'),
+
+ // reference to modalPopup to make sure only one instance is created
+ modalPopup: null,
+
+ showLoadingIndicator: function() {
+ if (!this.get('controller.isSubmitDisabled')) {
+ if (this.get('modalPopup')) {
+ this.get('modalPopup').hide();
+ this.set('modalPopup', null);
+ }
return;
}
- if(!this.get('controller.isSubmitDisabled')){
+ // don't create popup if it already exists
+ if (this.get('modalPopup')) {
return;
}
+ this.set('modalPopup', App.ModalPopup.show({
+ header: '',
- var opts = {
- lines: 13, // The number of lines to draw
- length: 7, // The length of each line
- width: 4, // The line thickness
- radius: 10, // The radius of the inner circle
- corners: 1, // Corner roundness (0..1)
- rotate: 0, // The rotation offset
- color: '#000', // #rgb or #rrggbb
- speed: 1, // Rounds per second
- trail: 60, // Afterglow percentage
- shadow: false, // Whether to render a shadow
- hwaccel: false, // Whether to use hardware acceleration
- className: 'spinner', // The CSS class to assign to the spinner
- zIndex: 2e9, // The z-index (defaults to 2000000000)
- top: 'auto', // Top position relative to parent in px
- left: 'auto' // Left position relative to parent in px
- };
- var target = $('#spinner')[0];
- this.set('spinner', new Spinner(opts).spin(target));
-
- /*var el = $('#spinner').children('b');
- el.css('display', 'inline-block');
- var deg = 0;
- var timeoutId = setInterval(function(){
- if(!$('#spinner').length){
- clearInterval(timeoutId);
- }
- deg += 15;
- deg %= 360;
- el.css('transform', 'rotate(' + deg + 'deg)');
- el.css('-ms-transform', 'rotate(' + deg + 'deg)');
- el.css('-o-transform', 'rotate(' + deg + 'deg)');
- el.css('-moz-transform', 'rotate(' + deg + 'deg)');
- el.css('-webkit-transform', 'rotate(' + deg + 'deg)');
- }, 80);*/
- }.observes('controller.isSubmitDisabled','controller.hasErrorOccurred')
+ showFooter: false,
+
+ showCloseButton: false,
+
+ bodyClass: Ember.View.extend({
+ templateName: require('templates/wizard/step8_log_popup'),
+
+ controllerBinding: 'App.router.wizardStep8Controller',
+
+ ajaxQueueLength: function() {
+ return this.get('controller.ajaxQueueLength');
+ }.property(),
+
+ ajaxQueueComplete: function() {
+ return this.get('ajaxQueueLength') - this.get('controller.ajaxQueueLeft');
+ }.property('controller.ajaxQueueLeft', 'ajaxQueueLength'),
+
+ barWidth: function () {
+ return 'width: ' + (this.get('ajaxQueueComplete') / this.get('ajaxQueueLength') * 100) + '%;';
+ }.property('ajaxQueueComplete', 'ajaxQueueLength'),
+
+ autoHide: function() {
+ if (this.get('controller.servicesInstalled')) {
+ this.get('parentView').hide();
+ }
+ }.observes('controller.servicesInstalled')
+ })
+ }));
+ }.observes('controller.isSubmitDisabled')
});
+
Modified: incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step9_view.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step9_view.js?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step9_view.js (original)
+++ incubator/ambari/branches/branch-1.2/ambari-web/app/views/wizard/step9_view.js Mon Feb 4 02:23:55 2013
@@ -146,78 +146,76 @@ App.HostStatusView = Em.View.extend({
return this.get('parentView.obj');
}.property('parentView.obj'),
- startedTasks:[], // initialized in didInsertElement
-
task: null, // set in showTaskLog; contains task info including stdout and stderr
/**
- * sort task array by request Id
+ * sort task array by Id
* @param tasks
* @return {Array}
*/
- sortTasksByRequest: function(tasks){
+ sortTasksById: function(tasks){
var result = [];
- var requestId = 1;
+ var id = 1;
for(var i = 0; i < tasks.length; i++){
- requestId = (tasks[i].Tasks.request_id > requestId) ? tasks[i].Tasks.request_id : requestId;
+ id = (tasks[i].Tasks.id > id) ? tasks[i].Tasks.id : id;
}
- while(requestId >= 1){
+ while(id >= 1){
for(var j = 0; j < tasks.length; j++){
- if(requestId == tasks[j].Tasks.request_id){
+ if(id == tasks[j].Tasks.id){
result.push(tasks[j]);
}
}
- requestId--;
+ id--;
}
result.reverse();
return result;
},
+ groupTasksByRole: function (tasks) {
+ var sortedTasks = [];
+ var taskRoles = tasks.mapProperty('Tasks.role').uniq();
+ for (var i = 0; i < taskRoles.length; i++) {
+ sortedTasks = sortedTasks.concat(tasks.filterProperty('Tasks.role', taskRoles[i]))
+ }
+ return sortedTasks;
+ },
+
visibleTasks: function () {
- var self=this;
+ var self = this;
self.set("isEmptyList", true);
if (this.get('category.value')) {
var filter = this.get('category.value');
- $.each(this.get("roles"),function(a,e){
-
- e.taskInfos.setEach("isVisible", false);
+ var tasks = this.get('tasks');
+ tasks.setEach("isVisible", false);
- if(filter == "all")
- {
- e.taskInfos.setEach("isVisible", true);
- }
- else if(filter == "pending")
- {
- e.taskInfos.filterProperty("status", "pending").setEach("isVisible", true);
- e.taskInfos.filterProperty("status", "queued").setEach("isVisible", true);
- }
- else if(filter == "in_progress")
- {
- e.taskInfos.filterProperty("status", "in_progress").setEach("isVisible", true);
- }
- else if(filter == "failed")
- {
- e.taskInfos.filterProperty("status", "failed").setEach("isVisible", true);
- }
- else if(filter == "completed")
- {
- e.taskInfos.filterProperty("status", "completed").setEach("isVisible", true);
- }
- else if(filter == "aborted")
- {
- e.taskInfos.filterProperty("status", "aborted").setEach("isVisible", true);
- }
- else if(filter == "timedout")
- {
- e.taskInfos.filterProperty("status", "timedout").setEach("isVisible", true);
- }
+ if (filter == "all") {
+ tasks.setEach("isVisible", true);
+ }
+ else if (filter == "pending") {
+ tasks.filterProperty("status", "pending").setEach("isVisible", true);
+ tasks.filterProperty("status", "queued").setEach("isVisible", true);
+ }
+ else if (filter == "in_progress") {
+ tasks.filterProperty("status", "in_progress").setEach("isVisible", true);
+ }
+ else if (filter == "failed") {
+ tasks.filterProperty("status", "failed").setEach("isVisible", true);
+ }
+ else if (filter == "completed") {
+ tasks.filterProperty("status", "completed").setEach("isVisible", true);
+ }
+ else if (filter == "aborted") {
+ tasks.filterProperty("status", "aborted").setEach("isVisible", true);
+ }
+ else if (filter == "timedout") {
+ tasks.filterProperty("status", "timedout").setEach("isVisible", true);
+ }
- if(e.taskInfos.filterProperty("isVisible", true).length >0){
- self.set("isEmptyList", false);
- }
- })
+ if (tasks.filterProperty("isVisible", true).length > 0) {
+ self.set("isEmptyList", false);
+ }
}
- }.observes('category'),
+ }.observes('category', 'tasks'),
categories: [
Ember.Object.create({value: 'all', label: 'All' }),
@@ -231,46 +229,40 @@ App.HostStatusView = Em.View.extend({
category: null,
- roles:function () {
- var roleArr = [];
+ tasks: function () {
+ var tasksArr = [];
var tasks = this.getStartedTasks(host);
- tasks = this.sortTasksByRequest(tasks);
+ tasks = this.sortTasksById(tasks);
+ tasks = this.groupTasksByRole(tasks);
if (tasks.length) {
- var _roles = tasks.mapProperty('Tasks.role').uniq();
- _roles.forEach(function (_role) {
- var taskInfos = [];
- var roleObj = {};
- roleObj.roleName = App.format.role(_role);
- tasks.filterProperty('Tasks.role', _role).forEach(function (_task) {
- var taskInfo = Ember.Object.create({});
- taskInfo.set('requestId', _task.Tasks.request_id);
- taskInfo.set('command', _task.Tasks.command.toLowerCase());
- taskInfo.set('status', App.format.taskStatus(_task.Tasks.status));
- taskInfo.set('url', _task.href);
- taskInfo.set('roleName', roleObj.roleName);
- taskInfo.set('isVisible', true);
- taskInfo.set('icon', '');
- if (taskInfo.get('status') == 'pending' || taskInfo.get('status') == 'queued') {
- taskInfo.set('icon', 'icon-cog');
- } else if (taskInfo.get('status') == 'in_progress') {
- taskInfo.set('icon', 'icon-cogs');
- } else if (taskInfo.get('status') == 'completed') {
- taskInfo.set('icon', ' icon-ok');
- } else if (taskInfo.get('status') == 'failed') {
- taskInfo.set('icon', 'icon-exclamation-sign');
- } else if (taskInfo.get('status') == 'aborted') {
- taskInfo.set('icon', 'icon-remove');
- } else if (taskInfo.get('status') == 'timedout') {
- taskInfo.set('icon', 'icon-time');
- }
- taskInfos.pushObject(taskInfo);
- }, this);
- roleObj.taskInfos = taskInfos;
- roleArr.pushObject(roleObj);
+ tasks.forEach(function (_task) {
+ var taskInfo = Ember.Object.create({});
+ taskInfo.set('id', _task.Tasks.id);
+ taskInfo.set('command', _task.Tasks.command.toLowerCase());
+ taskInfo.set('status', App.format.taskStatus(_task.Tasks.status));
+ taskInfo.set('role', App.format.role(_task.Tasks.role));
+ taskInfo.set('stderr', _task.Tasks.stderr);
+ taskInfo.set('stdout', _task.Tasks.stdout);
+ taskInfo.set('isVisible', true);
+ taskInfo.set('icon', '');
+ if (taskInfo.get('status') == 'pending' || taskInfo.get('status') == 'queued') {
+ taskInfo.set('icon', 'icon-cog');
+ } else if (taskInfo.get('status') == 'in_progress') {
+ taskInfo.set('icon', 'icon-cogs');
+ } else if (taskInfo.get('status') == 'completed') {
+ taskInfo.set('icon', ' icon-ok');
+ } else if (taskInfo.get('status') == 'failed') {
+ taskInfo.set('icon', 'icon-exclamation-sign');
+ } else if (taskInfo.get('status') == 'aborted') {
+ taskInfo.set('icon', 'icon-remove');
+ } else if (taskInfo.get('status') == 'timedout') {
+ taskInfo.set('icon', 'icon-time');
+ }
+ tasksArr.push(taskInfo);
}, this);
}
- return roleArr;
- }.property('startedTasks.@each'),
+ return tasksArr;
+ }.property('App.router.wizardStep9Controller.logTasksChangesCounter'),
backToTaskList: function(event, context) {
this.destroyClipBoard();
@@ -292,44 +284,28 @@ App.HostStatusView = Em.View.extend({
newdocument.close();
},
- toggleTaskLog:function (event, context) {
- if(this.isLogWrapHidden){
-
- var taskInfo = event.context;
- this.set("isLogWrapHidden",false);
-
- $(".task-detail-log-rolename")
- .html(taskInfo.roleName + " " + taskInfo.command)
+ openedTaskId: 0,
- $(".task-detail-status-ico")
- .removeClass()
- .addClass(taskInfo.status + " task-detail-status-ico " + taskInfo.icon);
-
- var url = (App.testMode) ? '/data/wizard/deploy/task_log.json' : taskInfo.url;
- $.ajax({
- url:url,
- dataType:'text',
- timeout:App.timeout,
- success:function (data) {
- var task = $.parseJSON(data);
- $(".stderr").html(task.Tasks.stderr);
- $(".stdout").html(task.Tasks.stdout);
- $(".modal").scrollTop(0);
- $(".modal-body").scrollTop(0);
- },
- error:function () {
- alert('Failed to retrieve task log');
- }
- });
- }else{
- this.set("isLogWrapHidden",true);
+ openedTask: function () {
+ if (!this.get('openedTaskId')) {
+ return Ember.Object.create();
}
+ return this.get('tasks').findProperty('id', this.get('openedTaskId'));
+ }.property('tasks', 'openedTaskId'),
-
-
-
-
+ toggleTaskLog: function (event, context) {
+ if (this.isLogWrapHidden) {
+ var taskInfo = event.context;
+ this.set("isLogWrapHidden", false);
+ this.set('openedTaskId', taskInfo.id);
+ $(".modal").scrollTop(0);
+ $(".modal-body").scrollTop(0);
+ } else {
+ this.set("isLogWrapHidden", true);
+ this.set('openedTaskId', 0);
+ }
},
+
textTrigger:function (event) {
if($(".task-detail-log-clipboard").length > 0)
{
Modified: incubator/ambari/branches/branch-1.2/ambari-web/config.coffee
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-web/config.coffee?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-web/config.coffee (original)
+++ incubator/ambari/branches/branch-1.2/ambari-web/config.coffee Mon Feb 4 02:23:55 2013
@@ -78,6 +78,10 @@ exports.config =
precompile: true
defaultExtension: 'hbs'
joinTo: 'javascripts/app.js' : /^app/
+ paths:
+ jquery: 'vendor/scripts/jquery-1.7.2.min.js'
+ handlebars: 'vendor/scripts/handlebars-1.0.0.beta.6.js'
+ ember: 'vendor/scripts/ember-latest.js'
server:
port: 3333
Modified: incubator/ambari/branches/branch-1.2/ambari-web/package.json
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-web/package.json?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-web/package.json (original)
+++ incubator/ambari/branches/branch-1.2/ambari-web/package.json Mon Feb 4 02:23:55 2013
@@ -18,7 +18,7 @@
"css-brunch":">= 1.0 < 1.5",
"uglify-js-brunch":">= 1.0 < 1.5",
"clean-css-brunch":">= 1.0 < 1.5",
- "ember-handlebars-brunch":"git://github.com/icholy/ember-handlebars-brunch.git",
+ "ember-precompiler-brunch":">= 1.0 < 1.5",
"less-brunch":">= 1.0 < 1.5"
},
"devDependencies":{
Modified: incubator/ambari/branches/branch-1.2/ambari-web/pom.xml
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-web/pom.xml?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-web/pom.xml (original)
+++ incubator/ambari/branches/branch-1.2/ambari-web/pom.xml Mon Feb 4 02:23:55 2013
@@ -19,7 +19,7 @@
<parent>
<groupId>org.apache.ambari</groupId>
<artifactId>ambari-project</artifactId>
- <version>1.2.0-SNAPSHOT</version>
+ <version>1.2.1-SNAPSHOT</version>
<relativePath>../ambari-project</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -27,7 +27,7 @@
<artifactId>ambari-web</artifactId>
<packaging>pom</packaging>
<name>Ambari Web</name>
- <version>1.0.3-SNAPSHOT</version>
+ <version>1.2.1-SNAPSHOT</version>
<description>Ambari Web</description>
<build>
<plugins>
@@ -80,6 +80,7 @@
<configuration>
<target name="ambari-web-compile">
<exec dir="${basedir}" executable="npm" failonerror="false">
+ <env key="PYTHON" value="python2.6" />
<arg value="install"/>
</exec>
<exec dir="${basedir}" executable="brunch" failonerror="false">
Modified: incubator/ambari/branches/branch-1.2/ambari-web/vendor/scripts/workflow_visualization.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-web/vendor/scripts/workflow_visualization.js?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-web/vendor/scripts/workflow_visualization.js (original)
+++ incubator/ambari/branches/branch-1.2/ambari-web/vendor/scripts/workflow_visualization.js Mon Feb 4 02:23:55 2013
@@ -1,390 +1,54 @@
/*
- * D3 Sankey diagram
- * another type to display graph
- */
-d3.sankey = function () {
- var sankey = {},
- nodeWidth = 24,
- nodePadding = 8,
- size = [1, 1],
- nodes = [],
- links = [],
- overlapLinksAtSources = false,
- overlapLinksAtTargets = false,
- minValue = 1;
-
- sankey.nodeWidth = function (_) {
- if (!arguments.length) return nodeWidth;
- nodeWidth = +_;
- return sankey;
- };
-
- sankey.nodePadding = function (_) {
- if (!arguments.length) return nodePadding;
- nodePadding = +_;
- return sankey;
- };
-
- sankey.nodes = function (_) {
- if (!arguments.length) return nodes;
- nodes = _;
- return sankey;
- };
-
- sankey.links = function (_) {
- if (!arguments.length) return links;
- links = _;
- return sankey;
- };
-
- sankey.size = function (_) {
- if (!arguments.length) return size;
- size = _;
- return sankey;
- };
-
- sankey.overlapLinksAtSources = function (_) {
- if (!arguments.length) return overlapLinksAtSources;
- overlapLinksAtSources = _;
- return sankey;
- };
-
- sankey.overlapLinksAtTargets = function (_) {
- if (!arguments.length) return overlapLinksAtTargets;
- overlapLinksAtTargets = _;
- return sankey;
- };
-
- sankey.minValue = function (_) {
- if (!arguments.length) return minValue;
- minValue = _;
- return sankey;
- };
-
- sankey.layout = function (iterations) {
- computeNodeLinks();
- computeNodeValues();
- computeNodeBreadths();
- computeNodeDepths(iterations);
- computeLinkDepths();
- return sankey;
- };
-
- sankey.relayout = function () {
- computeLinkDepths();
- return sankey;
- };
-
- sankey.link = function () {
- var curvature = .5;
-
- function link(d) {
- var x0 = d.source.x + d.source.dx,
- x1 = d.target.x,
- xi = d3.interpolateNumber(x0, x1),
- x2 = xi(curvature),
- x3 = xi(1 - curvature),
- y0 = d.source.y + (overlapLinksAtSources ? 0 : d.sy) + d.dy / 2,
- y1 = d.target.y + (overlapLinksAtTargets ? 0 : d.ty) + d.dy / 2;
- return "M" + x0 + "," + y0
- + "C" + x2 + "," + y0
- + " " + x3 + "," + y1
- + " " + x1 + "," + y1;
- }
-
- link.curvature = function (_) {
- if (!arguments.length) return curvature;
- curvature = +_;
- return link;
- };
-
- return link;
- };
-
- // Populate the sourceLinks and targetLinks for each node.
- // Also, if the source and target are not objects, assume they are indices.
- function computeNodeLinks() {
- nodes.forEach(function (node) {
- node.sourceLinks = [];
- node.targetLinks = [];
- });
- links.forEach(function (link) {
- var source = link.source,
- target = link.target;
- if (typeof source === "number") source = link.source = nodes[link.source];
- if (typeof target === "number") target = link.target = nodes[link.target];
- source.sourceLinks.push(link);
- target.targetLinks.push(link);
- if ("value" in link)
- link.value = Math.max(link.value, minValue);
- else
- link.value = minValue;
- });
- }
-
- // Compute the value (size) of each node by summing the associated links.
- function computeNodeValues() {
- nodes.forEach(function (node) {
- if ("value" in node)
- node.value = Math.max(node.value, minValue);
- else
- node.value = minValue;
- if (node.sourceLinks.length > 0) {
- if (overlapLinksAtSources)
- node.value = Math.max(node.value, d3.max(node.sourceLinks, value));
- else
- node.value = Math.max(node.value, d3.sum(node.sourceLinks, value));
- }
- if (node.targetLinks.length > 0) {
- if (overlapLinksAtTargets)
- node.value = Math.max(node.value, d3.max(node.targetLinks, value));
- else
- node.value = Math.max(node.value, d3.sum(node.targetLinks, value));
- }
- });
- }
-
- // Iteratively assign the breadth (x-position) for each node.
- // Nodes are assigned the maximum breadth of incoming neighbors plus one;
- // nodes with no incoming links are assigned breadth zero, while
- // nodes with no outgoing links are assigned the maximum breadth.
- function computeNodeBreadths() {
- var remainingNodes = nodes,
- nextNodes,
- x = 0;
-
- while (remainingNodes.length) {
- nextNodes = [];
- remainingNodes.forEach(function (node) {
- node.x = x;
- node.dx = nodeWidth;
- node.sourceLinks.forEach(function (link) {
- nextNodes.push(link.target);
- });
- });
- remainingNodes = nextNodes;
- ++x;
- }
-
- //
- moveSinksRight(x);
- scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
- }
-
- function moveSourcesRight() {
- nodes.forEach(function (node) {
- if (!node.targetLinks.length) {
- node.x = d3.min(node.sourceLinks, function (d) {
- return d.target.x;
- }) - 1;
- }
- });
- }
-
- function moveSinksRight(x) {
- nodes.forEach(function (node) {
- if (!node.sourceLinks.length) {
- node.x = x - 1;
- }
- });
- }
-
- function scaleNodeBreadths(kx) {
- nodes.forEach(function (node) {
- node.x *= kx;
- });
- }
-
- function computeNodeDepths(iterations) {
- var nodesByBreadth = d3.nest()
- .key(function (d) {
- return d.x;
- })
- .sortKeys(d3.ascending)
- .entries(nodes)
- .map(function (d) {
- return d.values;
- });
-
- //
- initializeNodeDepth();
- resolveCollisions();
- for (var alpha = 1; iterations > 0; --iterations) {
- relaxRightToLeft(alpha *= .99);
- resolveCollisions();
- relaxLeftToRight(alpha);
- resolveCollisions();
- }
-
- function initializeNodeDepth() {
- var ky = d3.min(nodesByBreadth, function (nodes) {
- return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
- });
-
- nodesByBreadth.forEach(function (nodes) {
- nodes.forEach(function (node, i) {
- node.y = i;
- node.dy = node.value * ky;
- });
- });
-
- links.forEach(function (link) {
- link.dy = link.value * ky;
- });
- }
-
- function relaxLeftToRight(alpha) {
- nodesByBreadth.forEach(function (nodes, breadth) {
- nodes.forEach(function (node) {
- if (node.targetLinks.length) {
- var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
- node.y += (y - center(node)) * alpha;
- }
- });
- });
-
- function weightedSource(link) {
- return center(link.source) * link.value;
- }
- }
-
- function relaxRightToLeft(alpha) {
- nodesByBreadth.slice().reverse().forEach(function (nodes) {
- nodes.forEach(function (node) {
- if (node.sourceLinks.length) {
- var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
- node.y += (y - center(node)) * alpha;
- }
- });
- });
-
- function weightedTarget(link) {
- return center(link.target) * link.value;
- }
- }
-
- function resolveCollisions() {
- nodesByBreadth.forEach(function (nodes) {
- var node,
- dy,
- y0 = 0,
- n = nodes.length,
- i;
-
- // Push any overlapping nodes down.
- nodes.sort(ascendingDepth);
- for (i = 0; i < n; ++i) {
- node = nodes[i];
- dy = y0 - node.y;
- if (dy > 0) node.y += dy;
- y0 = node.y + node.dy + nodePadding;
- }
-
- // If the bottommost node goes outside the bounds, push it back up.
- dy = y0 - nodePadding - size[1];
- if (dy > 0) {
- y0 = node.y -= dy;
-
- // Push any overlapping nodes back up.
- for (i = n - 2; i >= 0; --i) {
- node = nodes[i];
- dy = node.y + node.dy + nodePadding - y0;
- if (dy > 0) node.y -= dy;
- y0 = node.y;
- }
- }
- });
- }
-
- function ascendingDepth(a, b) {
- return a.y - b.y;
- }
- }
-
- function computeLinkDepths() {
- nodes.forEach(function (node) {
- node.sourceLinks.sort(ascendingTargetDepth);
- node.targetLinks.sort(ascendingSourceDepth);
- });
- nodes.forEach(function (node) {
- var sy = 0, ty = 0;
- node.sourceLinks.forEach(function (link) {
- link.sy = sy;
- sy += link.dy;
- });
- node.targetLinks.forEach(function (link) {
- link.ty = ty;
- ty += link.dy;
- });
- });
-
- function ascendingSourceDepth(a, b) {
- return a.source.y - b.source.y;
- }
-
- function ascendingTargetDepth(a, b) {
- return a.target.y - b.target.y;
- }
- }
-
- function center(node) {
- return node.y + node.dy / 2;
- }
-
- function value(link) {
- return link.value;
- }
-
- return sankey;
-};
-/*
* Example usage:
*
- * var dv = new DagViewer(false,'pig_5')
- * .setPhysicalParametrs(width,height[,charge,gravity])
- * .setData(dagSchema [,jobsData])
- * .drawDag([nodeSize,largeNodeSize,linkDistance]);
+ * var dv = new DagViewer('pig_5')
+ * .setData(workflowData,jobsData)
+ * .drawDag(svgWidth,svgHeight,nodeHeight);
*/
-function DagViewer(type, domId) {
- // initialize variables and force layout
+function DagViewer(domId) {
+ // initialize variables
this._nodes = new Array();
this._links = new Array();
this._numNodes = 0;
- this._type = type;
this._id = domId;
}
-DagViewer.prototype.setPhysicalParametrs = function (w, h, charge, gravity) {
- this._w = w;
- this._h = h;
- this._gravity = gravity || 0.1;
- this._charge = charge || -1000;
- this._force = d3.layout.force()
- .size([w, h])
- .gravity(this._gravity)
- .charge(this._charge);
- return this;
-}
-//set workflow schema
+// set workflow schema and job data
DagViewer.prototype.setData = function (wfData, jobData) {
// create map from entity names to nodes
var existingNodes = new Array();
var jobData = (jobData) ? jobData : new Array();
+ var minStartTime = 0;
+ if (jobData.length > 0)
+ minStartTime = jobData[0].submitTime;
+ var maxFinishTime = 0;
// iterate through job data
for (var i = 0; i < jobData.length; i++) {
+ minStartTime = Math.min(minStartTime, jobData[i].submitTime);
+ maxFinishTime = Math.max(maxFinishTime, jobData[i].submitTime + jobData[i].elapsedTime);
this._addNode(existingNodes, jobData[i].entityName, jobData[i]);
}
+ this._minStartTime = minStartTime;
+ this._maxFinishTime = maxFinishTime;
var dag = eval('(' + wfData + ')').dag;
+ this._sourceMarker = new Array();
+ this._targetMarker = new Array();
+ this._sourceMap = new Array();
// for each source node in the context, create links between it and its target nodes
for (var source in dag) {
- var sourceNode = this._getNode(source, existingNodes);
+ var sourceNode = null;
+ if (source in existingNodes)
+ sourceNode = existingNodes[source];
for (var i = 0; i < dag[source].length; i++) {
- var targetNode = this._getNode(dag[source][i], existingNodes);
+ var targetNode = null;
+ if (dag[source][i] in existingNodes)
+ targetNode = existingNodes[dag[source][i]];
this._addLink(sourceNode, targetNode);
}
}
return this;
}
+
// add a node to the nodes array and to a provided map of entity names to nodes
DagViewer.prototype._addNode = function (existingNodes, entityName, node) {
existingNodes[entityName] = node;
@@ -394,272 +58,284 @@ DagViewer.prototype._addNode = function
// add a link between sourceNode and targetNode
DagViewer.prototype._addLink = function (sourceNode, targetNode) {
+ // if source or target is null, add marker indicating unsubmitted job and return
+ if (sourceNode==null) {
+ if (targetNode==null)
+ return;
+ this._sourceMarker.push(targetNode);
+ return;
+ }
+ if (targetNode==null) {
+ this._targetMarker.push(sourceNode);
+ return;
+ }
+ // add link between nodes
var status = false;
if (sourceNode.status && targetNode.status)
status = true;
this._links.push({"source":sourceNode, "target":targetNode, "status":status, "value":sourceNode.output});
+ // add source to map of targets to sources
+ if (!(targetNode.name in this._sourceMap))
+ this._sourceMap[targetNode.name] = new Array();
+ this._sourceMap[targetNode.name].push(sourceNode);
}
-// get the node for an entity name, or add it if it doesn't exist
-// called after job nodes have all been added
-DagViewer.prototype._getNode = function (entityName, existingNodes) {
- if (!(entityName in existingNodes))
- this._addNode(existingNodes, entityName, { "name":entityName, "status":false, "input":1, "output":1});
- return existingNodes[entityName];
-}
// display the graph
-DagViewer.prototype.drawDag = function (nodeSize, largeNodeSize, linkDistance) {
- this._nodeSize = nodeSize || 18;
- this._largeNodeSize = largeNodeSize || 30;
- this._linkDistance = linkDistance || 100;
- // add new display to specified div
- this._svg = d3.select("div#" + this._id).append("svg:svg")
- .attr("width", this._w)
- .attr("height", this._h);
- // add sankey diagram or graph depending on type
- if (this._type)
- this._addSankey();
- else
- this._addDag();
+// rules of thumb: nodeHeight = 20, labelFontSize = 14, maxLabelWidth = 180
+// nodeHeight = 15, labelFontSize = 10, maxLabelWidth = 120
+// nodeHeight = 40, labelFontSize = 20, maxLabelWidth = 260
+// nodeHeight = 30, labelFontSize = 16
+DagViewer.prototype.drawDag = function (svgw, svgh, nodeHeight, labelFontSize, maxLabelWidth, axisPadding, svgPadding) {
+ this._addTimelineGraph(svgw, svgh, nodeHeight || 20, labelFontSize || 14, maxLabelWidth || 180, axisPadding || 30, svgPadding || 20);
return this;
}
-//draw the graph
-DagViewer.prototype._addDag = function () {
- var w = this._w;
- var h = this._h;
- var nodeSize = this._nodeSize;
- var largeNodeSize = this._largeNodeSize;
- var linkDistance = this._linkDistance;
- // add nodes and links to force layout
- this._force.nodes(this._nodes)
- .links(this._links)
- .linkDistance(this._linkDistance);
+// draw timeline graph
+DagViewer.prototype._addTimelineGraph = function (svgw, svgh, nodeHeight, labelFontSize, maxLabelWidth, axisPadding, svgPadding) {
+ // want to avoid having unnecessary scrollbars, so we need to size the div slightly larger than the svg
+ svgw = svgw - svgPadding;
+
+ var margin = {"top":10, "bottom":10, "left":30, "right":30};
+ var w = svgw - margin.left - margin.right;
+
+ var startTime = this._minStartTime;
+ var elapsedTime = this._maxFinishTime - this._minStartTime;
+ var x = d3.time.scale()
+ .domain([0, elapsedTime])
+ .range([0, w]);
+
+ // process nodes and determine their x and y positions, width and height
+ var minNodeSpacing = nodeHeight/2;
+ var ends = new Array();
+ var maxIndex = 0;
+ this._nodes = this._nodes.sort(function(a,b){return a.name.localeCompare(b.name);});
+ for (var i = 0; i < this._numNodes; i++) {
+ var d = this._nodes[i];
+ d.x = x(d.submitTime-startTime);
+ d.w = x(d.elapsedTime+d.submitTime-startTime) - x(d.submitTime-startTime);
+ if (d.w < nodeHeight/2) {
+ d.w = nodeHeight/2;
+ if (d.x + d.w > w)
+ d.x = w - d.w;
+ }
+ var effectiveX = d.x
+ var effectiveWidth = d.w;
+ if (d.w < maxLabelWidth) {
+ effectiveWidth = maxLabelWidth;
+ if (d.x + effectiveWidth > w)
+ effectiveX = w - effectiveWidth;
+ else if (d.x > 0)
+ effectiveX = d.x+(d.w-maxLabelWidth)/2;
+ }
+ // select "lane" (slot for y-position) for this node
+ // starting at the slot above the node's closest source node (or 0, if none exists)
+ // and moving down until a slot is found that has no nodes within minNodeSpacing of this node
+ // excluding slots that contain more than one source of this node
+ var index = 0;
+ var rejectIndices = new Array();
+ if (d.name in this._sourceMap) {
+ var sources = this._sourceMap[d.name];
+ var closestSource = sources[0];
+ var indices = new Array();
+ for (var j = 0; j < sources.length; j++) {
+ if (sources[j].index in indices)
+ rejectIndices[sources[j].index] = true;
+ indices[sources[j].index] = true;
+ if (sources[j].submitTime + sources[j].elapsedTime > closestSource.submitTime + closestSource.elapsedTime)
+ closestSource = sources[j];
+ }
+ index = Math.max(0, closestSource.index-1);
+ }
+ while ((index in ends) && ((index in rejectIndices) || (ends[index]+minNodeSpacing >= effectiveX))) {
+ index++
+ }
+ ends[index] = Math.max(effectiveX + effectiveWidth);
+ maxIndex = Math.max(maxIndex, index);
+ d.y = index*2*nodeHeight + axisPadding;
+ d.h = nodeHeight;
+ d.index = index;
+ }
+
+ var h = 2*axisPadding + 2*nodeHeight*(maxIndex+1);
+ d3.select("div#" + this._id)
+ .attr("style","width:"+(svgw+svgPadding)+"px;height:"+Math.min(svgh,h+margin.top+margin.bottom+svgPadding)+"px;overflow:auto;padding:none;");
+ svgh = h + margin.top + margin.bottom;
+ var svg = d3.select("div#" + this._id).append("svg:svg")
+ .attr("width", svgw+"px")
+ .attr("height", svgh+"px");
+ var svgg = svg.append("g")
+ .attr("transform", "translate("+margin.left+","+margin.top+")");
+
+ // create axes
+ var x = d3.time.scale()
+ .domain([0, elapsedTime])
+ .range([0, w]);
+ var tickFormatter = function(x) {
+ d = x.getTime();
+ if (d==0) { return "0" }
+ var seconds = Math.floor(parseInt(d) / 1000);
+ if ( seconds < 60 )
+ return seconds + "s";
+ var minutes = Math.floor(seconds / 60);
+ if ( minutes < 60 ) {
+ var x = seconds - 60*minutes;
+ return minutes + "m" + (x==0 ? "" : " " + x + "s");
+ }
+ var hours = Math.floor(minutes / 60);
+ if ( hours < 24 ) {
+ var x = minutes - 60*hours;
+ return hours + "h" + (x==0 ? "" : " " + x + "m");
+ }
+ var days = Math.floor(hours / 24);
+ if ( days < 7 ) {
+ var x = hours - 24*days;
+ return days + "d " + (x==0 ? "" : " " + x + "h");
+ }
+ var weeks = Math.floor(days / 7);
+ var x = days - 7*weeks;
+ return weeks + "w " + (x==0 ? "" : " " + x + "d");
+ };
+ var topAxis = d3.svg.axis()
+ .scale(x)
+ .orient("bottom")
+ .tickFormat(tickFormatter);
+ var bottomAxis = d3.svg.axis()
+ .scale(x)
+ .orient("top")
+ .tickFormat(tickFormatter);
+ svgg.append("g")
+ .attr("class", "x axis")
+ .call(topAxis);
+ svgg.append("g")
+ .attr("class", "x axis")
+ .call(bottomAxis)
+ .attr("transform", "translate(0,"+h+")");
+
+ // create a rectangle for each node
+ var boxes = svgg.append("svg:g").selectAll("rect")
+ .data(this._nodes)
+ .enter().append("svg:rect")
+ .attr("x", function(d) { return d.x; } )
+ .attr("y", function(d) { return d.y; } )
+ .attr("width", function(d) { return d.w; } )
+ .attr("height", function(d) { return d.h; } )
+ .attr("class", function (d) {
+ return "node " + (d.status ? " finished" : "");
+ })
+ .attr("id", function (d) {
+ return d.name;
+ });
+
// defs for arrowheads marked as to whether they link finished jobs or not
- this._svg.append("svg:defs").selectAll("marker")
+ svgg.append("svg:defs").selectAll("arrowmarker")
.data(["finished", "unfinished"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
- .attr("refX", nodeSize + 10)
- .attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
- .attr("d", "M0,-5L10,0L0,5");
+ .attr("d", "M0,-3L8,0L0,3");
+ // defs for unsubmitted node marker
+ svgg.append("svg:defs").selectAll("circlemarker")
+ .data(["circle"])
+ .enter().append("svg:marker")
+ .attr("id", String)
+ .attr("viewBox", "-2 -2 18 18")
+ .attr("markerWidth", 10)
+ .attr("markerHeight", 10)
+ .attr("refX", 10)
+ .attr("refY", 5)
+ .attr("orient", "auto")
+ .append("svg:circle")
+ .attr("cx", 5)
+ .attr("cy", 5)
+ .attr("r", 5);
+
+ // create dangling links representing unsubmitted jobs
+ var markerWidth = nodeHeight/2;
+ var sourceMarker = svgg.append("svg:g").selectAll("line")
+ .data(this._sourceMarker)
+ .enter().append("svg:line")
+ .attr("x1", function(d) { return d.x - markerWidth; } )
+ .attr("x2", function(d) { return d.x; } )
+ .attr("y1", function(d) { return d.y; } )
+ .attr("y2", function(d) { return d.y + 3; } )
+ .attr("class", "source mark")
+ .attr("marker-start", "url(#circle)");
+ var targetMarker = svgg.append("svg:g").selectAll("line")
+ .data(this._targetMarker)
+ .enter().append("svg:line")
+ .attr("x1", function(d) { return d.x + d.w + markerWidth; } )
+ .attr("x2", function(d) { return d.x + d.w; } )
+ .attr("y1", function(d) { return d.y + d.h; } )
+ .attr("y2", function(d) { return d.y + d.h - 3; } )
+ .attr("class", "target mark")
+ .attr("marker-start", "url(#circle)");
// create links between the nodes
- var lines = this._svg.append("svg:g").selectAll("line")
+ var lines = svgg.append("svg:g").selectAll("path")
.data(this._links)
- .enter().append("svg:line")
+ .enter().append("svg:path")
+ .attr("d", function(d) {
+ var s = d.source;
+ var t = d.target;
+ var x1 = s.x + s.w;
+ var x2 = t.x;
+ var y1 = s.y;
+ var y2 = t.y;
+ if (y1==y2) {
+ y1 += s.h/2;
+ y2 += t.h/2;
+ } else if (y1 < y2) {
+ y1 += s.h;
+ } else {
+ y2 += t.h;
+ }
+ return "M "+x1+" "+y1+" L "+((x2+x1)/2)+" "+((y2+y1)/2)+" L "+x2+" "+y2;
+ } )
.attr("class", function (d) {
return "link" + (d.status ? " finished" : "");
})
- .attr("marker-end", function (d) {
+ .attr("marker-mid", function (d) {
return "url(#" + (d.status ? "finished" : "unfinished") + ")";
});
-
- // create a circle for each node
- var circles = this._svg.append("svg:g").selectAll("circle")
- .data(this._nodes)
- .enter().append("svg:circle")
- .attr("r", nodeSize)
- .attr("class", function (d) {
- return "node " + (d.status ? " finished" : "");
- })
- .attr("id", function (d) {
- return d.name;
- })
- .on("dblclick", click)
- .call(this._force.drag);
-
+
// create text group for each node label
- var text = this._svg.append("svg:g").selectAll("g")
+ var text = svgg.append("svg:g").selectAll("g")
.data(this._nodes)
.enter().append("svg:g");
-
+
// add a shadow copy of the node label (will have a lighter color and thicker
- // stroke for legibility
+ // stroke for legibility)
text.append("svg:text")
- .attr("x", nodeSize + 3)
- .attr("y", ".31em")
- .attr("class", "shadow")
+ .attr("x", function(d) {
+ var goal = d.x + d.w/2;
+ var halfLabel = maxLabelWidth/2;
+ if (goal < halfLabel) return halfLabel; else if (goal > w-halfLabel) return w-halfLabel;
+ return goal;
+ } )
+ .attr("y", function(d) { return d.y + d.h + labelFontSize; } )
+ .attr("class", "joblabel shadow")
+ .attr("style", "font: "+labelFontSize+"px sans-serif")
.text(function (d) {
return d.name;
});
-
+
// add the main node label
text.append("svg:text")
- .attr("x", nodeSize + 3)
- .attr("y", ".31em")
+ .attr("x", function(d) {
+ var goal = d.x + d.w/2;
+ var halfLabel = maxLabelWidth/2;
+ if (goal < halfLabel) return halfLabel;
+ else if (goal > w-halfLabel) return w-halfLabel;
+ return goal;
+ } )
+ .attr("y", function(d) { return d.y + d.h + labelFontSize; } )
+ .attr("class", "joblabel")
+ .attr("style", "font: "+labelFontSize+"px sans-serif")
.text(function (d) {
return d.name;
});
-
- // add mouseover actions
- this._addMouseoverSelection(circles);
-
- // start the force layout
- this._force.on("tick", tick)
- .start();
-
- // on force tick, adjust positions of nodes, links, and text
- function tick() {
- circles.attr("transform", function (d) {
- if (d.x < largeNodeSize) d.x = largeNodeSize;
- if (d.y < largeNodeSize) d.y = largeNodeSize;
- if (d.x > w - largeNodeSize) d.x = w - largeNodeSize;
- if (d.y > h - largeNodeSize) d.y = h - largeNodeSize;
- return "translate(" + d.x + "," + d.y + ")";
- });
-
- lines.attr("x1", function (d) {
- return d.source.x
- })
- .attr("y1", function (d) {
- return d.source.y
- })
- .attr("x2", function (d) {
- return d.target.x
- })
- .attr("y2", function (d) {
- return d.target.y
- });
-
- text.attr("transform", function (d) {
- return "translate(" + d.x + "," + d.y + ")";
- });
- }
-
- // on double click, fix node in place or release it
- function click() {
- d3.select(this).attr("fixed", function (d) {
- if (d.fixed) {
- d.fixed = false
- } else {
- d.fixed = true
- }
- return d.fixed;
- });
- }
-}
-//define mouseover action on nodes
-DagViewer.prototype._addMouseoverSelection = function (nodes) {
- var nodeSize = this._nodeSize;
- var largeNodeSize = this._largeNodeSize;
- // on mouseover, change size of node
- nodes.on("mouseover", function (d) {
- d3.select(this).transition().attr("r", largeNodeSize);
- })
- .on("mouseout", function (d) {
- d3.select(this).transition().attr("r", nodeSize);
- });
-}
-//draw Sankey diagram
-DagViewer.prototype._addSankey = function () {
- var w = this._w;
- var h = this._h;
-
- // add svg group
- var svgg = this._svg.append("g");
-
- var color = d3.scale.category20();
-
- // create sankey
- var sankey = d3.sankey()
- .nodeWidth(15)
- .nodePadding(10)
- .size([w, h * 0.67]);
-
- // get sankey links
- var spath = sankey.link();
-
- // set sankey nodes and links and calculate their positions and sizes
- sankey
- .nodes(this._nodes)
- .links(this._links)
- .overlapLinksAtSources(true)
- .layout(32);
-
- // create links and set their attributes
- var slink = svgg.append("g").selectAll(".link")
- .data(this._links)
- .enter().append("path")
- .attr("class", "slink")
- .attr("d", spath)
- .style("stroke-width", function (d) {
- return Math.max(1, d.dy);
- })
- .sort(function (a, b) {
- return b.dy - a.dy;
- });
-
- // add mouseover text to links
- slink.append("title")
- .text(function (d) {
- return d.source.name + " - " + d.target.name + ": " + d.value;
- });
-
- // create node groups, set their attributes, and enable vertical dragging
- var snode = svgg.append("g").selectAll(".node")
- .data(this._nodes)
- .enter().append("g")
- .attr("class", "snode")
- .attr("transform", function (d) {
- return "translate(" + d.x + "," + d.y + ")";
- })
- .call(d3.behavior.drag()
- .origin(function (d) {
- return d;
- })
- .on("dragstart", function () {
- this.parentNode.appendChild(this);
- })
- .on("drag", dragmove));
-
- // add rectangles to node groups
- snode.append("rect")
- .attr("height", function (d) {
- return d.dy;
- })
- .attr("width", sankey.nodeWidth())
- .style("fill", function (d) {
- return d.color = color(d.name.replace(/ .*/, ""));
- })
- .style("stroke", function (d) {
- return d3.rgb(d.color).darker(2);
- })
- .append("title")
- .text(function (d) {
- return "info" in d ? d.info.join("\n") : d.name;
- });
-
- // add node labels
- snode.append("text")
- .attr("x", -6)
- .attr("y", function (d) {
- return d.dy / 2;
- })
- .attr("dy", ".35em")
- .attr("text-anchor", "end")
- .attr("transform", null)
- .text(function (d) {
- return d.name;
- })
- .filter(function (d) {
- return d.x < w / 2;
- })
- .attr("x", 6 + sankey.nodeWidth())
- .attr("text-anchor", "start");
-
- // add mouseover actions
- this._addMouseoverSelection(snode);
-
- // enable vertical dragging with recalculation of link placement
- function dragmove(d) {
- d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(h - d.dy, d3.event.y))) + ")");
- sankey.relayout();
- slink.attr("d", spath);
- }
}
Modified: incubator/ambari/branches/branch-1.2/ambari-web/vendor/styles/cubism.css
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/ambari-web/vendor/styles/cubism.css?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/ambari-web/vendor/styles/cubism.css (original)
+++ incubator/ambari/branches/branch-1.2/ambari-web/vendor/styles/cubism.css Mon Feb 4 02:23:55 2013
@@ -6,69 +6,57 @@
width: 100%;
}
-#dag_viewer line.link {
+#dag_viewer .axis path,
+#dag_viewer .axis line {
+ fill: none;
+ stroke: #000;
+ shape-rendering: crispEdges;
+}
+
+#dag_viewer line.link,
+#dag_viewer path.link {
fill: none;
stroke: #666;
stroke-width: 2.5px;
}
+#dag_viewer line.link.finished,
+#dag_viewer path.link.finished {
+ stroke: #444;
+}
+
#dag_viewer marker#finished {
- fill: green;
+ fill: #444;
}
-#dag_viewer line.link.finished {
- stroke: green;
+#dag_viewer marker#circle {
+ fill: #666;
+ stroke: none;
}
-#dag_viewer circle {
+#dag_viewer line.source.mark,
+#dag_viewer line.target.mark {
+ stroke: #666;
+ stroke-width: 2.5px;
+}
+
+#dag_viewer rect {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
-#dag_viewer circle.finished {
- fill: green;
+#dag_viewer rect.finished {
+ fill: #69BE28;
}
-#dag_viewer text {
- font: 20px sans-serif;
+#dag_viewer text.joblabel {
pointer-events: none;
-}
-
-#dag_viewer text.hover {
- font: 10px sans-serif;
-}
-
-#dag_viewer text.hover.shadow {
- stroke: #fff;
- stroke-width: 4px;
- stroke-opacity: .8;
+ text-anchor: middle;
}
#dag_viewer text.shadow {
stroke: #fff;
- stroke-width: 4px;
+ stroke-width: 3px;
stroke-opacity: .8;
}
-
-#dag_viewer .snode rect {
- cursor: move;
- fill-opacity: .9;
- shape-rendering: crispEdges;
-}
-
-#dag_viewer .snode text {
- pointer-events: none;
- text-shadow: 0 1px 0 #fff;
-}
-
-#dag_viewer .slink {
- fill: none;
- stroke: #000;
- stroke-opacity: .2;
-}
-
-#dag_viewer .slink:hover {
- stroke-opacity: .5;
-}
-
Modified: incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/create_ganglia_addon_rpm.sh
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/create_ganglia_addon_rpm.sh?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/create_ganglia_addon_rpm.sh (original)
+++ incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/create_ganglia_addon_rpm.sh Mon Feb 4 02:23:55 2013
@@ -27,7 +27,7 @@ if [[ -z "${BUILD_DIR}" ]]; then
fi
if [[ -z "${VERSION}" ]]; then
- VERSION="0.0.2.15"
+ VERSION="1.2.1.2"
fi
if [[ -z "${RELEASE}" ]]; then
Modified: incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/create_nagios_addon_rpm.sh
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/create_nagios_addon_rpm.sh?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/create_nagios_addon_rpm.sh (original)
+++ incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/create_nagios_addon_rpm.sh Mon Feb 4 02:23:55 2013
@@ -27,7 +27,7 @@ if [[ -z "${BUILD_DIR}" ]]; then
fi
if [[ -z "${VERSION}" ]]; then
- VERSION="0.0.2.15"
+ VERSION="1.2.1.2"
fi
if [[ -z "${RELEASE}" ]]; then
Modified: incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/hdp_mon_ganglia_addons.spec
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/hdp_mon_ganglia_addons.spec?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/hdp_mon_ganglia_addons.spec (original)
+++ incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/hdp_mon_ganglia_addons.spec Mon Feb 4 02:23:55 2013
@@ -24,7 +24,7 @@
Summary: Ganglia Add-ons for HDP Monitoring Dashboard
Name: hdp_mon_ganglia_addons
-Version: 0.0.2.15
+Version: 1.2.1.2
URL: http://hortonworks.com
Release: 1
License: Apache License, Version 2.0
Modified: incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/hdp_mon_nagios_addons.spec
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/hdp_mon_nagios_addons.spec?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/hdp_mon_nagios_addons.spec (original)
+++ incubator/ambari/branches/branch-1.2/contrib/addons/package/rpm/hdp_mon_nagios_addons.spec Mon Feb 4 02:23:55 2013
@@ -24,7 +24,7 @@
Summary: Nagios Add-ons for HDP Monitoring Dashboard
Name: hdp_mon_nagios_addons
-Version: 0.0.2.15
+Version: 1.2.1.2
URL: http://hortonworks.com
Release: 1
License: Apache License, Version 2.0
@@ -60,7 +60,7 @@ monitoring of a Hadoop Cluster
%__cp -rf scripts/* $RPM_BUILD_ROOT/%{nagioshdpscripts_dir}/
%__cp -rf plugins/* $RPM_BUILD_ROOT/%{nagiosplugin_dir}/
-echo "Alias /hdp %{_prefix}/share/hdp" >> $RPM_BUILD_ROOT/%{httpd_confdir}/hdp_mon_nagios_addons.conf
+echo "Alias /ambarinagios %{_prefix}/share/hdp" >> $RPM_BUILD_ROOT/%{httpd_confdir}/hdp_mon_nagios_addons.conf
echo "<Directory /usr/share/hdp>" >> $RPM_BUILD_ROOT/%{httpd_confdir}/hdp_mon_nagios_addons.conf
echo " Options None" >> $RPM_BUILD_ROOT/%{httpd_confdir}/hdp_mon_nagios_addons.conf
echo " AllowOverride None" >> $RPM_BUILD_ROOT/%{httpd_confdir}/hdp_mon_nagios_addons.conf
Modified: incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/pom.xml
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/pom.xml?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/pom.xml (original)
+++ incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/pom.xml Mon Feb 4 02:23:55 2013
@@ -20,7 +20,7 @@
<groupId>org.apache.ambari</groupId>
<artifactId>ambari-log4j</artifactId>
<packaging>jar</packaging>
- <version>1.0</version>
+ <version>1.2.1-SNAPSHOT</version>
<name>ambari-log4j</name>
<url>http://maven.apache.org</url>
<repositories>
Modified: incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/main/java/org/apache/ambari/log4j/hadoop/mapreduce/jobhistory/MapReduceJobHistoryUpdater.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/main/java/org/apache/ambari/log4j/hadoop/mapreduce/jobhistory/MapReduceJobHistoryUpdater.java?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/main/java/org/apache/ambari/log4j/hadoop/mapreduce/jobhistory/MapReduceJobHistoryUpdater.java (original)
+++ incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/main/java/org/apache/ambari/log4j/hadoop/mapreduce/jobhistory/MapReduceJobHistoryUpdater.java Mon Feb 4 02:23:55 2013
@@ -23,7 +23,12 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -127,7 +132,7 @@ public class MapReduceJobHistoryUpdater
workflowSelectPS =
connection.prepareStatement(
- "SELECT workflowId FROM " + WORKFLOW_TABLE + " where workflowId = ?"
+ "SELECT workflowContext FROM " + WORKFLOW_TABLE + " where workflowId = ?"
);
workflowPS =
@@ -154,6 +159,8 @@ public class MapReduceJobHistoryUpdater
"UPDATE " +
WORKFLOW_TABLE +
" SET " +
+ "workflowContext = ?, " +
+ "numJobsTotal = ?, " +
"lastUpdateTime = ?, " +
"duration = ? - (SELECT startTime FROM " +
WORKFLOW_TABLE +
@@ -597,6 +604,57 @@ public class MapReduceJobHistoryUpdater
return context;
}
+ public static void mergeEntries(Map<String, Set<String>> edges, List<WorkflowDagEntry> entries) {
+ if (entries == null)
+ return;
+ for (WorkflowDagEntry entry : entries) {
+ if (!edges.containsKey(entry.getSource()))
+ edges.put(entry.getSource(), new TreeSet<String>());
+ Set<String> targets = edges.get(entry.getSource());
+ targets.addAll(entry.getTargets());
+ }
+ }
+
+ public static WorkflowDag constructMergedDag(WorkflowContext workflowContext, WorkflowContext existingWorkflowContext) {
+ Map<String, Set<String>> edges = new TreeMap<String, Set<String>>();
+ if (existingWorkflowContext.getWorkflowDag() != null)
+ mergeEntries(edges, existingWorkflowContext.getWorkflowDag().getEntries());
+ if (workflowContext.getWorkflowDag() != null)
+ mergeEntries(edges, workflowContext.getWorkflowDag().getEntries());
+ WorkflowDag mergedDag = new WorkflowDag();
+ for (Entry<String,Set<String>> edge : edges.entrySet()) {
+ WorkflowDagEntry entry = new WorkflowDagEntry();
+ entry.setSource(edge.getKey());
+ entry.getTargets().addAll(edge.getValue());
+ mergedDag.addEntry(entry);
+ }
+ return mergedDag;
+ }
+
+ private static WorkflowContext getSanitizedWorkflow(WorkflowContext workflowContext, WorkflowContext existingWorkflowContext) {
+ WorkflowContext sanitizedWC = new WorkflowContext();
+ if (existingWorkflowContext == null) {
+ sanitizedWC.setWorkflowDag(workflowContext.getWorkflowDag());
+ sanitizedWC.setParentWorkflowContext(workflowContext.getParentWorkflowContext());
+ } else {
+ sanitizedWC.setWorkflowDag(constructMergedDag(existingWorkflowContext, workflowContext));
+ sanitizedWC.setParentWorkflowContext(existingWorkflowContext.getParentWorkflowContext());
+ }
+ return sanitizedWC;
+ }
+
+ private static String getWorkflowString(WorkflowContext sanitizedWC) {
+ String sanitizedWCString = null;
+ try {
+ ObjectMapper om = new ObjectMapper();
+ sanitizedWCString = om.writeValueAsString(sanitizedWC);
+ } catch (IOException e) {
+ e.printStackTrace();
+ sanitizedWCString = "";
+ }
+ return sanitizedWCString;
+ }
+
private void processJobSubmittedEvent(
PreparedStatement jobPS,
PreparedStatement workflowSelectPS, PreparedStatement workflowPS,
@@ -616,35 +674,35 @@ public class MapReduceJobHistoryUpdater
// Get workflow information
boolean insertWorkflow = false;
+ String existingContextString = null;
+ ResultSet rs = null;
try {
workflowSelectPS.setString(1, workflowContext.getWorkflowId());
workflowSelectPS.execute();
- ResultSet rs = workflowSelectPS.getResultSet();
- insertWorkflow = !rs.next();
+ rs = workflowSelectPS.getResultSet();
+ if (rs.next()) {
+ existingContextString = rs.getString(1);
+ } else {
+ insertWorkflow = true;
+ }
} catch (SQLException sqle) {
LOG.warn("workflow select failed with: ", sqle);
insertWorkflow = false;
+ } finally {
+ try {
+ if (rs != null)
+ rs.close();
+ } catch (SQLException e) {
+ LOG.error("Exception while closing ResultSet", e);
+ }
}
// Insert workflow
if (insertWorkflow) {
- WorkflowContext sanitizedWC = new WorkflowContext();
- sanitizedWC.setWorkflowDag(workflowContext.getWorkflowDag());
- sanitizedWC.setParentWorkflowContext(workflowContext.getParentWorkflowContext());
-
- String sanitizedWCString = null;
- try {
- ObjectMapper om = new ObjectMapper();
- sanitizedWCString = om.writeValueAsString(sanitizedWC);
- } catch (IOException e) {
- e.printStackTrace();
- sanitizedWCString = "";
- }
-
workflowPS.setString(1, workflowContext.getWorkflowId());
workflowPS.setString(2, workflowContext.getWorkflowName());
- workflowPS.setString(3, sanitizedWCString);
+ workflowPS.setString(3, getWorkflowString(getSanitizedWorkflow(workflowContext, null)));
workflowPS.setString(4, historyEvent.getUserName());
workflowPS.setLong(5, historyEvent.getSubmitTime());
workflowPS.setLong(6, historyEvent.getSubmitTime());
@@ -653,10 +711,22 @@ public class MapReduceJobHistoryUpdater
LOG.debug("Successfully inserted workflowId = " +
workflowContext.getWorkflowId());
} else {
- workflowUpdateTimePS.setLong(1, historyEvent.getSubmitTime());
- workflowUpdateTimePS.setLong(2, historyEvent.getSubmitTime());
- workflowUpdateTimePS.setString(3, workflowContext.getWorkflowId());
- workflowUpdateTimePS.setString(4, workflowContext.getWorkflowId());
+ ObjectMapper om = new ObjectMapper();
+ WorkflowContext existingWorkflowContext = null;
+ try {
+ if (existingContextString != null)
+ existingWorkflowContext = om.readValue(existingContextString.getBytes(), WorkflowContext.class);
+ } catch (IOException e) {
+ LOG.warn("Couldn't read existing workflow context for " + workflowContext.getWorkflowId(), e);
+ }
+
+ WorkflowContext sanitizedWC = getSanitizedWorkflow(workflowContext, existingWorkflowContext);
+ workflowUpdateTimePS.setString(1, getWorkflowString(sanitizedWC));
+ workflowUpdateTimePS.setLong(2, sanitizedWC.getWorkflowDag().size());
+ workflowUpdateTimePS.setLong(3, historyEvent.getSubmitTime());
+ workflowUpdateTimePS.setLong(4, historyEvent.getSubmitTime());
+ workflowUpdateTimePS.setString(5, workflowContext.getWorkflowId());
+ workflowUpdateTimePS.setString(6, workflowContext.getWorkflowId());
workflowUpdateTimePS.executeUpdate();
LOG.debug("Successfully updated workflowId = " +
workflowContext.getWorkflowId());
Modified: incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/test/java/org/apache/ambari/TestJobHistoryParsing.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/test/java/org/apache/ambari/TestJobHistoryParsing.java?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/test/java/org/apache/ambari/TestJobHistoryParsing.java (original)
+++ incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/test/java/org/apache/ambari/TestJobHistoryParsing.java Mon Feb 4 02:23:55 2013
@@ -30,6 +30,7 @@ import org.apache.ambari.eventdb.model.W
import org.apache.ambari.log4j.hadoop.mapreduce.jobhistory.MapReduceJobHistoryUpdater;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.mapred.JobHistory;
+import org.apache.hadoop.mapreduce.JobID;
import org.apache.hadoop.tools.rumen.JobSubmittedEvent;
import org.apache.hadoop.util.StringUtils;
@@ -65,20 +66,42 @@ public class TestJobHistoryParsing exten
test("id_= 0-1", "something.name", "1=0", adj);
}
+ public void test3() {
+ String s = "`~!@#$%^&*()-_=+[]{}|,.<>/?;:'\"";
+ test(s, s, s, new HashMap<String,String[]>());
+ }
+
+ public void test4() {
+ Map<String,String[]> adj = new HashMap<String,String[]>();
+ adj.put("X", new String[] {});
+ test("", "jobName", "X", adj);
+ }
+
public void test(String workflowId, String workflowName, String workflowNodeName, Map<String,String[]> adjacencies) {
Configuration conf = new Configuration();
setProperties(conf, workflowId, workflowName, workflowNodeName, adjacencies);
String log = log("JOB", new String[] {ID, NAME, NODE, ADJ},
new String[] {conf.get(ID_PROP), conf.get(NAME_PROP), conf.get(NODE_PROP), JobHistory.JobInfo.getWorkflowAdjacencies(conf)});
ParsedLine line = new ParsedLine(log);
- JobSubmittedEvent event = new JobSubmittedEvent(null, "", "", 0l, "", null, "", line.get(ID), line.get(NAME), line.get(NODE), line.get(ADJ));
+ JobID jobid = new JobID("id", 1);
+ JobSubmittedEvent event = new JobSubmittedEvent(jobid, workflowName, "", 0l, "", null, "", line.get(ID), line.get(NAME), line.get(NODE), line.get(ADJ));
WorkflowContext context = MapReduceJobHistoryUpdater.buildWorkflowContext(event);
- assertEquals("Didn't recover workflowId", workflowId, context.getWorkflowId());
+
+ String resultingWorkflowId = workflowId;
+ if (workflowId.isEmpty())
+ resultingWorkflowId = jobid.toString().replace("job_", "mr_");
+ assertEquals("Didn't recover workflowId", resultingWorkflowId, context.getWorkflowId());
assertEquals("Didn't recover workflowName", workflowName, context.getWorkflowName());
assertEquals("Didn't recover workflowNodeName", workflowNodeName, context.getWorkflowEntityName());
- assertEquals("Got incorrect number of adjacencies", adjacencies.size(), context.getWorkflowDag().getEntries().size());
+
+ Map<String,String[]> resultingAdjacencies = adjacencies;
+ if (resultingAdjacencies.size() == 0) {
+ resultingAdjacencies = new HashMap<String,String[]>();
+ resultingAdjacencies.put(workflowNodeName, new String[] {});
+ }
+ assertEquals("Got incorrect number of adjacencies", resultingAdjacencies.size(), context.getWorkflowDag().getEntries().size());
for (WorkflowDagEntry entry : context.getWorkflowDag().getEntries()) {
- String[] sTargets = adjacencies.get(entry.getSource());
+ String[] sTargets = resultingAdjacencies.get(entry.getSource());
assertNotNull("No original targets for " + entry.getSource(), sTargets);
List<String> dTargets = entry.getTargets();
assertEquals("Got incorrect number of targets for " + entry.getSource(), sTargets.length, dTargets.size());
Added: incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/test/java/org/apache/ambari/TestMapReduceJobHistoryUpdater.java
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/test/java/org/apache/ambari/TestMapReduceJobHistoryUpdater.java?rev=1442010&view=auto
==============================================================================
--- incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/test/java/org/apache/ambari/TestMapReduceJobHistoryUpdater.java (added)
+++ incubator/ambari/branches/branch-1.2/contrib/ambari-log4j/src/test/java/org/apache/ambari/TestMapReduceJobHistoryUpdater.java Mon Feb 4 02:23:55 2013
@@ -0,0 +1,101 @@
+/**
+ * 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.
+ */
+package org.apache.ambari;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.ambari.eventdb.model.WorkflowContext;
+import org.apache.ambari.eventdb.model.WorkflowDag;
+import org.apache.ambari.eventdb.model.WorkflowDag.WorkflowDagEntry;
+import org.apache.ambari.log4j.hadoop.mapreduce.jobhistory.MapReduceJobHistoryUpdater;
+
+/**
+ *
+ */
+public class TestMapReduceJobHistoryUpdater extends TestCase {
+ public void testDagMerging() {
+ WorkflowDag dag1 = new WorkflowDag();
+ dag1.addEntry(getEntry("a", "b", "c"));
+ dag1.addEntry(getEntry("b", "d"));
+ WorkflowContext one = new WorkflowContext();
+ one.setWorkflowDag(dag1);
+
+ WorkflowDag dag2 = new WorkflowDag();
+ dag2.addEntry(getEntry("a", "d"));
+ dag2.addEntry(getEntry("c", "e"));
+ WorkflowContext two = new WorkflowContext();
+ two.setWorkflowDag(dag2);
+
+ WorkflowDag emptyDag = new WorkflowDag();
+ WorkflowContext three = new WorkflowContext();
+ three.setWorkflowDag(emptyDag);
+
+ WorkflowDag mergedDag = new WorkflowDag();
+ mergedDag.addEntry(getEntry("a", "b", "c", "d"));
+ mergedDag.addEntry(getEntry("b", "d"));
+ mergedDag.addEntry(getEntry("c", "e"));
+
+ assertEquals(mergedDag, MapReduceJobHistoryUpdater.constructMergedDag(one, two));
+ assertEquals(mergedDag, MapReduceJobHistoryUpdater.constructMergedDag(two, one));
+
+ // test blank dag
+ assertEquals(dag1, MapReduceJobHistoryUpdater.constructMergedDag(three, one));
+ assertEquals(dag1, MapReduceJobHistoryUpdater.constructMergedDag(one, three));
+ assertEquals(dag2, MapReduceJobHistoryUpdater.constructMergedDag(three, two));
+ assertEquals(dag2, MapReduceJobHistoryUpdater.constructMergedDag(two, three));
+
+ // test null dag
+ assertEquals(dag1, MapReduceJobHistoryUpdater.constructMergedDag(new WorkflowContext(), one));
+ assertEquals(dag1, MapReduceJobHistoryUpdater.constructMergedDag(one, new WorkflowContext()));
+ assertEquals(dag2, MapReduceJobHistoryUpdater.constructMergedDag(new WorkflowContext(), two));
+ assertEquals(dag2, MapReduceJobHistoryUpdater.constructMergedDag(two, new WorkflowContext()));
+
+ // test same dag
+ assertEquals(dag1, MapReduceJobHistoryUpdater.constructMergedDag(one, one));
+ assertEquals(dag2, MapReduceJobHistoryUpdater.constructMergedDag(two, two));
+ assertEquals(emptyDag, MapReduceJobHistoryUpdater.constructMergedDag(three, three));
+ }
+
+ private static WorkflowDagEntry getEntry(String source, String... targets) {
+ WorkflowDagEntry entry = new WorkflowDagEntry();
+ entry.setSource(source);
+ for (String target : targets) {
+ entry.addTarget(target);
+ }
+ return entry;
+ }
+
+ private static void assertEquals(WorkflowDag dag1, WorkflowDag dag2) {
+ assertEquals(dag1.size(), dag2.size());
+ List<WorkflowDagEntry> entries1 = dag1.getEntries();
+ List<WorkflowDagEntry> entries2 = dag2.getEntries();
+ assertEquals(entries1.size(), entries2.size());
+ for (int i = 0; i < entries1.size(); i++) {
+ WorkflowDagEntry e1 = entries1.get(i);
+ WorkflowDagEntry e2 = entries2.get(i);
+ assertEquals(e1.getSource(), e2.getSource());
+ List<String> t1 = e1.getTargets();
+ List<String> t2 = e2.getTargets();
+ assertEquals(t1.size(), t2.size());
+ for (int j = 0; j < t1.size(); j++) {
+ assertEquals(t1.get(j), t2.get(j));
+ }
+ }
+ }
+}
Modified: incubator/ambari/branches/branch-1.2/pom.xml
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/branch-1.2/pom.xml?rev=1442010&r1=1442009&r2=1442010&view=diff
==============================================================================
--- incubator/ambari/branches/branch-1.2/pom.xml (original)
+++ incubator/ambari/branches/branch-1.2/pom.xml Mon Feb 4 02:23:55 2013
@@ -21,7 +21,7 @@
<artifactId>ambari</artifactId>
<packaging>pom</packaging>
<name>Ambari Main</name>
- <version>1.2.0-SNAPSHOT</version>
+ <version>1.2.1-SNAPSHOT</version>
<description>Ambari</description>
<pluginRepositories>
<pluginRepository>
@@ -115,7 +115,6 @@
<excludes>
<exclude>**/*.json</exclude>
<exclude>derby.log</exclude>
- <exclude>CHANGES.txt</exclude>
<exclude>AMBARI-666-CHANGES.txt</exclude>
<exclude>pass.txt</exclude>
<exclude>contrib/addons/test/dataServices/jmx/data/cluster_configuration.json.nohbase</exclude>
@@ -125,7 +124,6 @@
<exclude>.git/</exclude>
<exclude>**/.gitignore</exclude>
<exclude>**/.gitattributes</exclude>
-
<!--gitignore content-->
<exclude>.DS_Store</exclude>
<exclude>.iml/</exclude>
@@ -137,6 +135,11 @@
<exclude>.hg</exclude>
<exclude>.hgignore</exclude>
<exclude>.hgtags</exclude>
+
+
+ <!--Python Mock library (BSD license)-->
+ <exclude>ambari-common/src/test/python/mock/**</exclude>
+
</excludes>
</configuration>
</plugin>