Adding Standard Out and other Job Detail things

This includes the work from the Job Detail standard out panel along with fixes from feedback from UX and other UI devs.
This includes:
- result panel CSS fixes
- selected row styling
- stop job button functionality
- websockets for job detail
- everthing for adding stdout to the job detail: panel, styling, toggle, and websockets
This commit is contained in:
Jared Tabor
2016-03-02 13:38:23 -08:00
parent de83011954
commit fdaee0242a
8 changed files with 509 additions and 424 deletions

View File

@@ -214,9 +214,7 @@
} }
#job-detail-container { #job-detail-container {
position: relative;
padding-left: 15px;
padding-right: 7px;
.well { .well {
overflow: hidden; overflow: hidden;
} }

View File

@@ -975,7 +975,7 @@ var tower = angular.module('Tower', [
$log.debug("sending status to standard out"); $log.debug("sending status to standard out");
$rootScope.$emit('JobStatusChange-jobStdout', data); $rootScope.$emit('JobStatusChange-jobStdout', data);
} else if ($state.is('jobDetail')) { } if ($state.is('jobDetail')) {
$rootScope.$emit('JobStatusChange-jobDetails', data); $rootScope.$emit('JobStatusChange-jobDetails', data);
} else if ($state.is('dashboard')) { } else if ($state.is('dashboard')) {
$rootScope.$emit('JobStatusChange-home', data); $rootScope.$emit('JobStatusChange-home', data);

View File

@@ -235,7 +235,7 @@ export default
} }
if (newActivePlay) { if (newActivePlay) {
scope.activePlay = newActivePlay; scope.activePlay = newActivePlay;
scope.jobData.plays[scope.activePlay].playActiveClass = 'List-tableRow--selected'; scope.jobData.plays[scope.activePlay].playActiveClass = 'JobDetail-tableRow--selected';
} }
} }
}; };
@@ -265,7 +265,7 @@ export default
} }
if (newActiveTask) { if (newActiveTask) {
scope.activeTask = newActiveTask; scope.activeTask = newActiveTask;
scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].taskActiveClass = 'List-tableRow--selected'; scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].taskActiveClass = 'JobDetail-tableRow--selected';
} }
} }
}; };
@@ -793,7 +793,7 @@ export default
scope.selectedPlay = id; scope.selectedPlay = id;
scope.plays.forEach(function(play, idx) { scope.plays.forEach(function(play, idx) {
if (play.id === scope.selectedPlay) { if (play.id === scope.selectedPlay) {
scope.plays[idx].playActiveClass = 'List-tableRow--selected'; scope.plays[idx].playActiveClass = 'JobDetail-tableRow--selected';
} }
else { else {
scope.plays[idx].playActiveClass = ''; scope.plays[idx].playActiveClass = '';
@@ -940,7 +940,7 @@ export default
scope.selectedTask = id; scope.selectedTask = id;
scope.tasks.forEach(function(task, idx) { scope.tasks.forEach(function(task, idx) {
if (task.id === scope.selectedTask) { if (task.id === scope.selectedTask) {
scope.tasks[idx].taskActiveClass = 'List-tableRow--selected'; scope.tasks[idx].taskActiveClass = 'JobDetail-tableRow--selected';
} }
else { else {
scope.tasks[idx].taskActiveClass = ''; scope.tasks[idx].taskActiveClass = '';
@@ -1143,7 +1143,7 @@ export default
return function(params) { return function(params) {
var scope = params.scope, var scope = params.scope,
resize = params.resize, resize = params.resize,
width, height, svg_height, svg_width, svg_radius, graph_data = []; graph_data = [];
// Ready the data // Ready the data
if (scope.host_summary.ok) { if (scope.host_summary.ok) {
@@ -1192,7 +1192,8 @@ export default
element = $("#graph-section"), element = $("#graph-section"),
colors, total,job_detail_chart; colors, total,job_detail_chart;
colors = ['#60D66F', '#FF9900','#FF0000','#ff5850']; // colors = ['#60D66F', '#FF9900','#FF0000','#ff5850'];
colors = _.map(dataset, function(d){return d.color});
total = d3.sum(dataset.map(function(d) { total = d3.sum(dataset.map(function(d) {
return d.value; return d.value;
})); }));
@@ -1221,6 +1222,7 @@ export default
"font-weight":400, "font-weight":400,
"src": "url(/static/assets/OpenSans-Regular.ttf)" "src": "url(/static/assets/OpenSans-Regular.ttf)"
}); });
d3.select(element.find(".nv-label text")[0]) d3.select(element.find(".nv-label text")[0])
.attr("class", "DashboardGraphs-hostStatusLabel--successful") .attr("class", "DashboardGraphs-hostStatusLabel--successful")
.style({ .style({
@@ -1228,7 +1230,7 @@ export default
"text-anchor": "start", "text-anchor": "start",
"font-size": "16px", "font-size": "16px",
"text-transform" : "uppercase", "text-transform" : "uppercase",
"fill" : '#3CB878', "fill" : colors[0],
"src": "url(/static/assets/OpenSans-Regular.ttf)" "src": "url(/static/assets/OpenSans-Regular.ttf)"
}); });
d3.select(element.find(".nv-label text")[1]) d3.select(element.find(".nv-label text")[1])
@@ -1238,7 +1240,7 @@ export default
"text-anchor" : "end !imporant", "text-anchor" : "end !imporant",
"font-size": "16px", "font-size": "16px",
"text-transform" : "uppercase", "text-transform" : "uppercase",
"fill" : "#FF9900", "fill" : colors[1],
"src": "url(/static/assets/OpenSans-Regular.ttf)" "src": "url(/static/assets/OpenSans-Regular.ttf)"
}); });
d3.select(element.find(".nv-label text")[2]) d3.select(element.find(".nv-label text")[2])
@@ -1248,7 +1250,7 @@ export default
"text-anchor" : "end !imporant", "text-anchor" : "end !imporant",
"font-size": "16px", "font-size": "16px",
"text-transform" : "uppercase", "text-transform" : "uppercase",
"fill" : "#FF0000", "fill" : colors[2],
"src": "url(/static/assets/OpenSans-Regular.ttf)" "src": "url(/static/assets/OpenSans-Regular.ttf)"
}); });
d3.select(element.find(".nv-label text")[3]) d3.select(element.find(".nv-label text")[3])
@@ -1258,7 +1260,7 @@ export default
"text-anchor" : "end !imporant", "text-anchor" : "end !imporant",
"font-size": "16px", "font-size": "16px",
"text-transform" : "uppercase", "text-transform" : "uppercase",
"fill" : "#ff5850", "fill" : colors[3],
"src": "url(/static/assets/OpenSans-Regular.ttf)" "src": "url(/static/assets/OpenSans-Regular.ttf)"
}); });
return job_detail_chart; return job_detail_chart;

View File

@@ -3,7 +3,19 @@
@import '../shared/branding/colors.less'; @import '../shared/branding/colors.less';
@import '../shared/branding/colors.default.less'; @import '../shared/branding/colors.default.less';
.JobDetail{
display: flex;
flex-direction: row;
}
.JobDetail-leftSide{ .JobDetail-leftSide{
flex: 1 0 auto;
width: 50%;
padding-right: 20px;
}
.JobDetail-rightSide{
flex: 1 0 auto;
width: 50%; width: 50%;
} }
@@ -49,6 +61,7 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: row; flex-direction: row;
padding-top: 25px;
} }
.JobDetail-resultRow{ .JobDetail-resultRow{
@@ -56,6 +69,10 @@
display: flex; display: flex;
} }
.JobDetail-resultRowLabel{
text-transform: uppercase;
}
.JobDetail-resultRow label{ .JobDetail-resultRow label{
color: @default-interface-txt; color: @default-interface-txt;
font-size: 14px; font-size: 14px;
@@ -64,14 +81,26 @@
} }
.JobDetail-resultRow--variables{ .JobDetail-resultRow--variables{
width: 90%; width: 100%;
display: block; display: flex;
flex-direction: column;
padding-left:15px;
}
.JobDetail-extraVars{
text-transform: none;
}
.JobDetail-extraVarsLabel{
margin-left:-15px;
padding-bottom: 15px;
} }
.JobDetail-resultRowText{ .JobDetail-resultRowText{
width: 40%; width: 40%;
flex: 1 0 auto; flex: 1 0 auto;
padding:0px; padding:0px;
text-transform: none;
} }
.JobDetail-searchHeaderRow{ .JobDetail-searchHeaderRow{
@@ -101,7 +130,7 @@
.JobDetail-tableToggle.active{ .JobDetail-tableToggle.active{
background-color: @default-link; background-color: @default-link;
border: 1px solid @default-link; border: 1px solid @default-link;
color: @toggle-selected-text; color: @default-bg;
} }
.JobDetail-tableToggle--left{ .JobDetail-tableToggle--left{
@@ -127,7 +156,27 @@
padding-left: 10px; padding-left: 10px;
} }
.JobDetail-tableRow--selected,
.JobDetail-tableRow--selected > :first-child{
border-left: 5px solid @list-row-select-bord;
}
.JobDetail-tableRow--selected > :first-child > .JobDetail-statusIcon{
margin-left: -5px;
}
.JobDetail-statusIcon--results{
padding-left: 0px;
padding-right: 10px;
}
.JobDetail-graphSection{ .JobDetail-graphSection{
height: 320px; height: 320px;
width:100%; width:100%;
} }
.JobDetail-stdoutActionButton--active{
flex:none;
width:0px;
padding-right: 0px;
}

View File

@@ -43,6 +43,7 @@ export default
scope.plays = []; scope.plays = [];
scope.parseType = 'yaml'; scope.parseType = 'yaml';
scope.previousTaskFailed = false; scope.previousTaskFailed = false;
$scope.stdoutFullScreen = false;
scope.$watch('job_status', function(job_status) { scope.$watch('job_status', function(job_status) {
if (job_status && job_status.explanation && job_status.explanation.split(":")[0] === "Previous Task Failed") { if (job_status && job_status.explanation && job_status.explanation.split(":")[0] === "Previous Task Failed") {
@@ -202,7 +203,7 @@ export default
scope.processing = false; scope.processing = false;
scope.lessStatus = false; scope.lessStatus = false;
scope.lessDetail = false; scope.lessDetail = false;
scope.lessEvents = false; scope.lessEvents = true;
scope.host_summary = {}; scope.host_summary = {};
scope.host_summary.ok = 0; scope.host_summary.ok = 0;
@@ -557,7 +558,7 @@ export default
}); });
}); });
if (scope.activeTask && scope.jobData.plays[scope.activePlay] && scope.jobData.plays[scope.activePlay].tasks[scope.activeTask]) { if (scope.activeTask && scope.jobData.plays[scope.activePlay] && scope.jobData.plays[scope.activePlay].tasks[scope.activeTask]) {
scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].taskActiveClass = 'List-tableRow--selected'; scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].taskActiveClass = 'JobDetail-tableRow--selected';
} }
scope.$emit('LoadHosts'); scope.$emit('LoadHosts');
}) })
@@ -677,7 +678,7 @@ export default
scope.host_summary.failed; scope.host_summary.failed;
}); });
if (scope.activePlay && scope.jobData.plays[scope.activePlay]) { if (scope.activePlay && scope.jobData.plays[scope.activePlay]) {
scope.jobData.plays[scope.activePlay].playActiveClass = 'List-tableRow--selected'; scope.jobData.plays[scope.activePlay].playActiveClass = 'JobDetail-tableRow--selected';
} }
scope.$emit('LoadTasks', events_url); scope.$emit('LoadTasks', events_url);
}) })
@@ -981,11 +982,11 @@ export default
scope.toggleLessStatus = function() { scope.toggleLessStatus = function() {
if (!scope.lessStatus) { if (!scope.lessStatus) {
$('#job-status-form .toggle-show').slideUp(200); $('#job-status-form').slideUp(200);
scope.lessStatus = true; scope.lessStatus = true;
} }
else { else {
$('#job-status-form .toggle-show').slideDown(200); $('#job-status-form').slideDown(200);
scope.lessStatus = false; scope.lessStatus = false;
} }
}; };
@@ -1009,6 +1010,7 @@ export default
else { else {
$('#events-summary').slideDown(200); $('#events-summary').slideDown(200);
scope.lessEvents = false; scope.lessEvents = false;
DrawGraph({scope:scope});
} }
}; };
@@ -1432,16 +1434,10 @@ export default
$scope.$emit('LoadJob'); $scope.$emit('LoadJob');
}; };
scope.editHost = function(id) { // Click binding for the expand/collapse button on the standard out log
HostsEdit({ $scope.toggleStdoutFullscreen = function() {
host_scope: scope, $scope.stdoutFullScreen = !$scope.stdoutFullScreen;
group_scope: null, }
host_id: id,
inventory_id: scope.job.inventory,
mode: 'edit', // 'add' or 'edit'
selected_group_id: null
});
};
scope.editSchedule = function() { scope.editSchedule = function() {
// We need to get the schedule's ID out of the related links // We need to get the schedule's ID out of the related links

View File

@@ -1,8 +1,11 @@
<div class="tab-pane" id="jobs-detail"> <div class="tab-pane" id="jobs-detail">
<div ng-cloak id="htmlTemplate"> <div ng-cloak id="htmlTemplate" class="JobDetail">
<div class="row" style="position: relative;">
<div id="job-detail-container" class="JobDetail-leftSide"> <!--beginning of job-detail-container (left side) -->
<div id="job-results-panel" class="JobDetail-resultsContainer Panel"> <div id="job-detail-container" class="JobDetail-leftSide" ng-class="{'JobDetail-stdoutActionButton--active': stdoutFullScreen}">
<!--beginning of results-->
<div id="job-results-panel" class="JobDetail-resultsContainer Panel" ng-show="!stdoutFullScreen">
<div class="JobDetail-panelHeader"> <div class="JobDetail-panelHeader">
<div class="JobDetail-expandContainer"> <div class="JobDetail-expandContainer">
<a class="JobDetail-panelHeaderText" ng-show="lessStatus" href="" ng-click="toggleLessStatus()"> <a class="JobDetail-panelHeaderText" ng-show="lessStatus" href="" ng-click="toggleLessStatus()">
@@ -13,18 +16,20 @@
</a> </a>
</div> </div>
<div class="JobDetail-actions"> <div class="JobDetail-actions">
<button id="submit-action" class="List-actionButton JobDetail-launchButton" data-placement="top" mode="all" ng-click="relaunchJob()" aw-tool-tip="Start a job using this template" data-original-title="" title=""><i class="fa fa-rocket"></i> </button> <button id="relaunch-job-button" class="List-actionButton JobDetail-launchButton" data-placement="top" mode="all" ng-click="relaunchJob()" aw-tool-tip="Relaunch using the same parameters" data-original-title="" title=""><i class="fa fa-rocket"></i> </button>
<button id="delete-action" class="List-actionButton List-actionButton--delete JobDetail-launchButton" data-placement="top" ng-click="deleteJobTemplate(job_template.id, job_template.name)" aw-tool-tip="Delete template" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button> <button id="cancel-job-button" class="List-actionButton List-actionButton--delete JobDetail-launchButton" data-placement="top" ng-click="deleteJob()" ng-show="job_status.status == 'running' || job_status.status=='pending' " aw-tool-tip="Cancel" data-original-title="" title=""><i class="fa fa-minus-circle"></i> </button>
<button id="delete-job-button" class="List-actionButton List-actionButton--delete JobDetail-launchButton" data-placement="top" ng-click="deleteJob()" ng-hide="job_status.status == 'running' || job_status.status == 'pending' " aw-tool-tip="Delete" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
</div> </div>
</div> </div>
<div class="form-horizontal JobDetail-resultsDetails" role="form" id="job-status-form"> <div class="form-horizontal JobDetail-resultsDetails" role="form" id="job-status-form">
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started"> <div class="form-group JobDetail-resultRow toggle-show">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Status</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Status</label>
<div class="JobDetail-resultRowText"><i class="fa icon-job-{{ job_status.status }}"></i> {{ job_status.status_label }}</div> <div class="JobDetail-resultRowText"><i class="JobDetail-statusIcon--results fa icon-job-{{ job_status.status }}"></i> {{ job_status.status_label }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.explanation"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.explanation">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 col-xs-12">Explanation</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 col-xs-12">Explanation</label>
<div class="JobDetail-resultRowText col-lg-10 col-md-10 col-sm-10 col-xs-9 job_status_explanation" <div class="JobDetail-resultRowText col-lg-10 col-md-10 col-sm-10 col-xs-9 job_status_explanation"
ng-show="!previousTaskFailed" ng-bind-html="job_status.explanation"></div> ng-show="!previousTaskFailed" ng-bind-html="job_status.explanation"></div>
<div class="JobDetail-resultRowText col-lg-10 col-md-10 col-sm-10 col-xs-9 job_status_explanation" <div class="JobDetail-resultRowText col-lg-10 col-md-10 col-sm-10 col-xs-9 job_status_explanation"
@@ -45,114 +50,115 @@
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.traceback"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.traceback">
<label class="col-lg-2 col-md-12 col-sm-12 col-xs-12">Results Traceback</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-12 col-sm-12 col-xs-12">Results Traceback</label>
<div class="JobDetail-resultRowText col-lg-10 col-md-12 col-sm-12 col-xs-12 job_status_traceback" ng-bind-html="job_status.traceback"></div> <div class="JobDetail-resultRowText col-lg-10 col-md-12 col-sm-12 col-xs-12 job_status_traceback" ng-bind-html="job_status.traceback"></div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_template_name"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job_template_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Template</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Template</label>
<div class="JobDetail-resultRowText"> <div class="JobDetail-resultRowText">
<a href="{{ job_template_url }}" aw-tool-tip="Edit the job template" data-placement="top">{{ job_template_name }}</a> <a href="{{ job_template_url }}" aw-tool-tip="Edit the job template" data-placement="top">{{ job_template_name }}</a>
</div> </div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Started</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Started</label>
<div class="JobDetail-resultRowText">{{ job_status.started | date:'MM/dd/yy HH:mm:ss' }}</div> <div class="JobDetail-resultRowText">{{ job_status.started | date:'MM/dd/yy HH:mm:ss' }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_type"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job_type">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Job Type</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Job Type</label>
<div class="JobDetail-resultRowText">{{ job_type }}</div> <div class="JobDetail-resultRowText">{{ job_type }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Finished</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Finished</label>
<div class="JobDetail-resultRowText">{{ job_status.finished | date:'MM/dd/yy HH:mm:ss' }}</div> <div class="JobDetail-resultRowText">{{ job_status.finished | date:'MM/dd/yy HH:mm:ss' }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="created_by"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="created_by">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Launched By</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Launched By</label>
<div class="JobDetail-resultRowText"> <div class="JobDetail-resultRowText">
<a href="{{ users_url }}" aw-tool-tip="Edit the User" data-placement="top">{{ created_by }}</a> <a href="{{ users_url }}" aw-tool-tip="Edit the User" data-placement="top">{{ created_by }}</a>
</div> </div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Elapsed</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Elapsed</label>
<div class="JobDetail-resultRowText">{{ job_status.elapsed }}</div> <div class="JobDetail-resultRowText">{{ job_status.elapsed }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="scheduled_by"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="scheduled_by">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Launched By</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Launched By</label>
<div class="JobDetail-resultRowText"> <div class="JobDetail-resultRowText">
<a href aw-tool-tip="Edit the Schedule" data-placement="top" ng-click="editSchedule()">{{scheduled_by}}</a> <a href aw-tool-tip="Edit the Schedule" data-placement="top" ng-click="editSchedule()">{{scheduled_by}}</a>
</div> </div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="inventory_name"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="inventory_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Inventory</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Inventory</label>
<div class="JobDetail-resultRowText"> <div class="JobDetail-resultRowText">
<a href="{{ inventory_url }}" aw-tool-tip="Edit the inventory" data-placement="top">{{ inventory_name }}</a> <a href="{{ inventory_url }}" aw-tool-tip="Edit the inventory" data-placement="top">{{ inventory_name }}</a>
</div> </div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="project_name"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="project_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Project</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Project</label>
<div class="JobDetail-resultRowText"> <div class="JobDetail-resultRowText">
<a href="{{ project_url }}" aw-tool-tip="Edit the project" data-placement="top">{{ project_name }}</a> <a href="{{ project_url }}" aw-tool-tip="Edit the project" data-placement="top">{{ project_name }}</a>
</div> </div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.playbook"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job.playbook">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Playbook</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Playbook</label>
<div class="JobDetail-resultRowText">{{ job.playbook }}</div> <div class="JobDetail-resultRowText">{{ job.playbook }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="credential_name"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="credential_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Machine Credential</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Machine Credential</label>
<div class="JobDetail-resultRowText JobDetail-resultRowText"> <div class="JobDetail-resultRowText JobDetail-resultRowText">
<a href="{{ credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ credential_name }}</a> <a href="{{ credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ credential_name }}</a>
</div> </div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="cloud_credential_name"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="cloud_credential_name">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Cloud Credential</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Cloud Credential</label>
<div class="JobDetail-resultRowText"> <div class="JobDetail-resultRowText">
<a href="{{ cloud_credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ cloud_credential_name }}</a> <a href="{{ cloud_credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ cloud_credential_name }}</a>
</div> </div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.forks"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job.forks">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Forks</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Forks</label>
<div class="JobDetail-resultRowText">{{ job.forks }}</div> <div class="JobDetail-resultRowText">{{ job.forks }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.limit"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job.limit">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Limit</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Limit</label>
<div class="JobDetail-resultRowText">{{ job.limit }}</div> <div class="JobDetail-resultRowText">{{ job.limit }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="verbosity"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="verbosity">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Verbosity</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Verbosity</label>
<div class="JobDetail-resultRowText">{{ verbosity }}</div> <div class="JobDetail-resultRowText">{{ verbosity }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.job_tags"> <div class="form-group JobDetail-resultRow toggle-show" ng-show="job.job_tags">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Job Tags</label> <label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Job Tags</label>
<div class="JobDetail-resultRowText">{{ job.job_tags }}</div> <div class="JobDetail-resultRowText">{{ job.job_tags }}</div>
</div> </div>
<div class="form-group JobDetail-resultRow JobDetail-resultRow--variables toggle-show" ng-show="variables"> <div class="form-group JobDetail-resultRow JobDetail-resultRow--variables toggle-show" ng-show="variables">
<label class="col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Extra Variables</label> <label class="JobDetail-resultRowLabel JobDetail-extraVarsLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label">Extra Variables</label>
<textarea rows="6" ng-model="variables" name="variables" id="pre-formatted-variables"></textarea> <textarea rows="6" ng-model="variables" name="variables" class="JobDetail-extraVars" id="pre-formatted-variables"></textarea>
</div> </div>
</div> </div>
</div> </div>
<!--- JobDetail-results----------------------------------------------> <!--- end of results-->
<div id="job-detail-panel" class="JobDetail-resultsContainer Panel"> <!--beginning of details-->
<div id="job-detail-panel" class="JobDetail-resultsContainer Panel" ng-show="!stdoutFullScreen">
<div class="JobDetail-panelHeader"> <div class="JobDetail-panelHeader">
<div class="JobDetail-expandContainer"> <div class="JobDetail-expandContainer">
<a class="JobDetail-panelHeaderText" ng-show="lessDetail" href="" ng-click="toggleLessDetail()"> <a class="JobDetail-panelHeaderText" ng-show="lessDetail" href="" ng-click="toggleLessDetail()">
@@ -217,12 +223,13 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="scroll-spinner" id="playsMoreRows"><i class="fa fa-cog fa-spin"></i></div> <div class="scroll-spinner" id="playsMoreRows">
<i class="fa fa-cog fa-spin"></i>
</div> </div>
<!-- end of plays section--> </div>
<!-- end of plays section of details-->
<div id="task-section" class="section" > <div id="task-section" class="section" >
<div class="JobDetail-searchHeaderRow"> <div class="JobDetail-searchHeaderRow">
<div class="JobDetail-searchContainer form-group"> <div class="JobDetail-searchContainer form-group">
<div class="search-name"> <div class="search-name">
@@ -243,7 +250,7 @@
<table id="tasks-table-header" class="table table-condensed"> <table id="tasks-table-header" class="table table-condensed">
<thead> <thead>
<tr> <tr>
<th class="List-tableHeader col-lg-3 col-md-3 col-sm-6 col-xs-4">Tasks</div> <th class="List-tableHeader col-lg-3 col-md-3 col-sm-6 col-xs-4">Tasks</th>
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3">Started</th> <th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3">Started</th>
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3">Elapsed</th> <th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3">Elapsed</th>
<th class="List-tableHeader JobDetail-tableHeader col-lg-4 col-md-3 hidden-xs hidden-sm">Host Status</th> <th class="List-tableHeader JobDetail-tableHeader col-lg-4 col-md-3 hidden-xs hidden-sm">Host Status</th>
@@ -303,6 +310,7 @@
</div> </div>
<div class="scroll-spinner" id="tasksMoreRows"><i class="fa fa-cog fa-spin"></i></div> <div class="scroll-spinner" id="tasksMoreRows"><i class="fa fa-cog fa-spin"></i></div>
</div><!-- section --> </div><!-- section -->
<!--end of tasks section of details-->
<div id="task-hosts-section" class="section"> <div id="task-hosts-section" class="section">
<div class="JobDetail-searchHeaderRow"> <div class="JobDetail-searchHeaderRow">
@@ -324,10 +332,9 @@
<table class="table table-condensed"> <table class="table table-condensed">
<thead> <thead>
<tr> <tr>
<th class="List-tableHeader col-lg-3 col-md-3 col-sm-3 col-xs-3">Hosts</th> <th class="List-tableHeader col-lg-4 col-md-3 col-sm-3 col-xs-3">Hosts</th>
<th class="List-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3">Item</th> <th class="List-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3">Item</th>
<th class="List-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3">Message</th> <th class="List-tableHeader JobDetail-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3">Message</th>
<th class="List-tableHeader col-lg-2 col-md-1 col-sm-1 col-xs-1">Actions</th>
</tr> </tr>
</thead> </thead>
</table> </table>
@@ -337,12 +344,9 @@
<table class="table"> <table class="table">
<tbody> <tbody>
<tr class="List-tableRow cursor-pointer" ng-class-odd="'List-tableRow--oddRow'" ng-class-even="'List-tableRow--evenRow'" ng-repeat="result in results = (hostResults) track by $index"> <tr class="List-tableRow cursor-pointer" ng-class-odd="'List-tableRow--oddRow'" ng-class-even="'List-tableRow--evenRow'" ng-repeat="result in results = (hostResults) track by $index">
<td class="List-tableCell col-lg-3 col-md-3 col-sm-3 col-xs-3 status-column"><a href="" ng-click="viewHostResults(result.id)" aw-tool-tip="Event ID: {{ result.id }}<br \>Status: {{ result.status_text }}. Click for details" data-placement="top"><i ng-show="result.status_text != 'Unreachable'" class="JobDetail-statusIcon fa icon-job-{{ result.status }}"></i><span ng-show="result.status_text != 'Unreachable'">{{ result.name }}</span><i ng-show="result.status_text == 'Unreachable'" class="JobDetail-statusIcon fa icon-job-unreachable"></i><span ng-show="result.status_text == 'Unreachable'">{{ result.name }}</span></a></td> <td class="List-tableCell col-lg-4 col-md-3 col-sm-3 col-xs-3 status-column"><a href="" ng-click="viewHostResults(result.id)" aw-tool-tip="Event ID: {{ result.id }}<br \>Status: {{ result.status_text }}. Click for details" data-placement="top"><i ng-show="result.status_text != 'Unreachable'" class="JobDetail-statusIcon fa icon-job-{{ result.status }}"></i><span ng-show="result.status_text != 'Unreachable'">{{ result.name }}</span><i ng-show="result.status_text == 'Unreachable'" class="JobDetail-statusIcon fa icon-job-unreachable"></i><span ng-show="result.status_text == 'Unreachable'">{{ result.name }}</span></a></td>
<td class="List-tableCell col-lg-3 col-md-4 col-sm-3 col-xs-3 item-column">{{ result.item }}</td> <td class="List-tableCell col-lg-3 col-md-4 col-sm-3 col-xs-3 item-column">{{ result.item }}</td>
<td class="List-tableCell col-lg-3 col-md-4 col-sm-3 col-xs-3">{{ result.msg }}</td> <td class="List-tableCell col-lg-3 col-md-4 col-sm-3 col-xs-3">{{ result.msg }}</td>
<td class="List-actionButtonCell List-tableCell col-lg-1 col-md-1 col-sm-1 col-xs-1">
<button class="List-actionButton " ng-show="result.host_id" data-placement="top" ng-click="editHost(result.host_id)" aw-tool-tip="Edit host" data-original-title="" title=""><i class="fa fa-pencil"></i> </button>
</td>
</tr> </tr>
<tr ng-show="results.length === 0 && waiting"> <tr ng-show="results.length === 0 && waiting">
<td colspan="5" class="col-lg-12 loading-info">Waiting...</td> <td colspan="5" class="col-lg-12 loading-info">Waiting...</td>
@@ -358,15 +362,20 @@
</div> </div>
<div class="scroll-spinner" id="hostResultsMoreRows"><i class="fa fa-cog fa-spin"></i></div> <div class="scroll-spinner" id="hostResultsMoreRows"><i class="fa fa-cog fa-spin"></i></div>
</div> </div>
<!--end of hosts section of details-->
</div> </div>
</div> </div>
<!--end of details-->
<div id="events-summary-panel" class="JobDetail-resultsContainer Panel">
<div id="summary-well-top-section">
<div id="hide-summary-button" style="display: hidden;">
<a href="" class="btn btn-xs btn-primary" ng-click="toggleSummary('hide')" aw-tool-tip="Hide summary" data-placement="top"><i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!--end of job-detail-container (left side)-->
<!--beginning of stdout-->
<div class="JobDetail-rightSide">
<!--beginning of events summary-->
<div id="events-summary-panel" class="JobDetail-resultsContainer Panel" ng-show="!stdoutFullScreen">
<div class="JobDetail-panelHeader"> <div class="JobDetail-panelHeader">
<div class="JobDetail-expandContainer"> <div class="JobDetail-expandContainer">
<a class="JobDetail-panelHeaderText" ng-show="lessEvents" href="" ng-click="toggleLessEvents()"> <a class="JobDetail-panelHeaderText" ng-show="lessEvents" href="" ng-click="toggleLessEvents()">
@@ -377,8 +386,11 @@
</a> </a>
</div> </div>
</div> </div>
<div id="events-summary">
<div id="events-summary" style="display:none">
<div id="hosts-summary-section" class="section"> <div id="hosts-summary-section" class="section">
<div class="JobDetail-searchHeaderRow"> <div class="JobDetail-searchHeaderRow">
<div class="JobDetail-searchContainer form-group"> <div class="JobDetail-searchContainer form-group">
<div class="search-name"> <div class="search-name">
@@ -394,32 +406,31 @@
</div> </div>
</div> </div>
</div> </div>
<div class="table-header"> <div class="table-header">
<table class="table table-condensed"> <table class="table table-condensed">
<thead> <thead>
<tr> <tr>
<th class="List-tableHeader col-lg-5 col-md-6 col-sm-6 col-xs-6">Hosts</th> <th class="List-tableHeader col-lg-6 col-md-6 col-sm-6 col-xs-6">Hosts</th>
<th class="List-tableHeader col-lg-5 col-md-5 col-sm-5 col-xs-5">Completed Tasks</th> <th class="List-tableHeader JobDetail-tableHeader col-lg-6 col-md-5 col-sm-5 col-xs-5">Completed Tasks</th>
<th class="List-tableHeader col-lg-2 col-md-1 col-sm-1 col-xs-1">Actions</th>
</tr> </tr>
</thead> </thead>
</table> </table>
</div> </div>
<div id="hosts-summary-table" class="table-detail" lr-infinite-scroll="hostSummariesScrollDown" scroll-threshold="10" time-threshold="500"> <div id="hosts-summary-table" class="table-detail" lr-infinite-scroll="hostSummariesScrollDown" scroll-threshold="10" time-threshold="500">
<table class="table"> <table class="table">
<tbody> <tbody>
<tr class="List-tableRow" ng-repeat="host in summaryList = (hosts) track by $index" id="{{ host.id }}" ng-class-even="'List-tableRow--evenRow'" ng-class-odd="'List-tableRow--oddRow'"> <tr class="List-tableRow" ng-repeat="host in summaryList = (hosts) track by $index" id="{{ host.id }}" ng-class-even="'List-tableRow--evenRow'" ng-class-odd="'List-tableRow--oddRow'">
<td class="List-tableCell name col-lg-6 col-md-6 col-sm-6 col-xs-6"><a href="" ng-click="hostEventsViewer(host.id, host.name)" aw-tool-tip="View events" data-placement="top">{{ host.name }}</a></td> <td class="List-tableCell name col-lg-6 col-md-6 col-sm-6 col-xs-6">
<td class="List-tableCell col-lg-5 col-md-5 col-sm-5 col-xs-5 badge-column"> <a href="" ng-click="hostEventsViewer(host.id, host.name)" aw-tool-tip="View events" data-placement="top">{{ host.name }}</a>
</td>
<td class="List-tableCell col-lg-6 col-md-5 col-sm-5 col-xs-5 badge-column">
<a href="" ng-click="hostEventsViewer(host.id, host.name, 'ok')" aw-tool-tip="{{ host.okTip }}" data-tip-watch="host.okTip" data-placement="top" ng-hide="host.ok == 0"><span class="badge successful-hosts">{{ host.ok }}</span></a> <a href="" ng-click="hostEventsViewer(host.id, host.name, 'ok')" aw-tool-tip="{{ host.okTip }}" data-tip-watch="host.okTip" data-placement="top" ng-hide="host.ok == 0"><span class="badge successful-hosts">{{ host.ok }}</span></a>
<a href="" ng-click="hostEventsViewer(host.id, host.name, 'changed')" aw-tool-tip="{{ host.changedTip }}" data-tip-watch="host.changedTip" data-placement="top" ng-hide="host.changed == 0"><span class="badge changed-hosts">{{ host.changed }}</span></a> <a href="" ng-click="hostEventsViewer(host.id, host.name, 'changed')" aw-tool-tip="{{ host.changedTip }}" data-tip-watch="host.changedTip" data-placement="top" ng-hide="host.changed == 0"><span class="badge changed-hosts">{{ host.changed }}</span></a>
<a href="" ng-click="hostEventsViewer(host.id, host.name, 'unreachable')" aw-tool-tip="{{ host.unreachableTip }}" data-tip-watch="host.unreachableTip" data-placement="top" ng-hide="host.unreachable == 0"><span class="badge unreachable-hosts">{{ host.unreachable }}</span></a> <a href="" ng-click="hostEventsViewer(host.id, host.name, 'unreachable')" aw-tool-tip="{{ host.unreachableTip }}" data-tip-watch="host.unreachableTip" data-placement="top" ng-hide="host.unreachable == 0"><span class="badge unreachable-hosts">{{ host.unreachable }}</span></a>
<a href="" ng-click="hostEventsViewer(host.id, host.name, 'failed')" aw-tool-tip="{{ host.failedTip }}" data-tip-watch="host.failedTip" data-placement="top" ng-hide="host.failed == 0"><span class="badge failed-hosts">{{ host.failed }}</span></a></td> <a href="" ng-click="hostEventsViewer(host.id, host.name, 'failed')" aw-tool-tip="{{ host.failedTip }}" data-tip-watch="host.failedTip" data-placement="top" ng-hide="host.failed == 0"><span class="badge failed-hosts">{{ host.failed }}</span></a>
<td class="List-actionButtonCell List-tableCell col-lg-2 col-md-1 col-sm-1 col-xs-1">
<button class="List-actionButton " ng-show="host.id" data-placement="top" ng-click="editHost(host.id)" aw-tool-tip="Edit host" data-original-title="" title=""><i class="fa fa-pencil"></i></button>
</td> </td>
</tr> </tr>
<tr ng-show="summaryList.length === 0 && waiting"> <tr ng-show="summaryList.length === 0 && waiting">
<td colspan="5" class="col-lg-12 loading-info">Waiting...</td> <td colspan="5" class="col-lg-12 loading-info">Waiting...</td>
@@ -433,18 +444,41 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="scroll-spinner" id="hostSummariesMoreRows"><i class="fa fa-cog fa-spin"></i></div>
<div class="scroll-spinner" id="hostSummariesMoreRows">
<i class="fa fa-cog fa-spin"></i>
</div>
</div><!-- section --> </div><!-- section -->
<div id="graph-section" class="JobDetail-graphSection"> <div id="graph-section" class="JobDetail-graphSection">
<svg width="100%" height="100%"></svg> <svg width="100%" height="100%"></svg>
</div> </div>
</div> </div>
<!--end of events summary-->
</div> </div>
</div> <!--end of job-detail-container--> <!-- end of events summary-->
</div><!-- col-md-5 -->
<div class="JobDetail-stdoutPanel Panel">
<div class="StandardOut-panelHeader">
<div class="StandardOut-panelHeaderText">STANDARD OUT</div>
<div class="StandardOut-panelHeaderActions">
<button class="StandardOut-actionButton" aw-tool-tip="Toggle Output" data-placement="top" ng-class="{'StandardOut-actionButton--active': stdoutFullScreen}" ng-click="toggleStdoutFullscreen()">
<i class="fa fa-arrows-alt"></i>
</button>
<a href="/api/v1/jobs/{{ job.id }}/stdout?format=txt_download&token={{ token }}">
<button class="StandardOut-actionButton" aw-tool-tip="Download Output" data-placement="top">
<i class="fa fa-download"></i>
</button>
</a>
</div>
</div>
<standard-out-log stdout-endpoint="job.related.stdout"></standard-out-log>
</div>
</div>
<!--end of stdout-->
</div> </div>
</div> </div>
<div ng-include="'/static/partials/eventviewer.html'"></div> <div ng-include="'/static/partials/eventviewer.html'"></div>

View File

@@ -161,9 +161,6 @@
@db-graph-axis: @default-border; @db-graph-axis: @default-border;
@db-graph-axis-label: @default-interface-txt; @db-graph-axis-label: @default-interface-txt;
//job detail
@toggle-selected-text: #eeeeee;
// panel // panel
@panel-bg: @default-bg; @panel-bg: @default-bg;
@panel-border: @default-border; @panel-border: @default-border;

View File

@@ -21,6 +21,15 @@ export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'Proce
// Open up a socket for events depending on the type of job // Open up a socket for events depending on the type of job
function openSockets() { function openSockets() {
if ($state.current.name == 'jobDetail') {
$log.debug("socket watching on job_events-" + job_id);
$rootScope.event_socket.on("job_events-" + job_id, function() {
$log.debug("socket fired on job_events-" + job_id);
if (api_complete) {
event_queue++;
}
});
}
if ($state.current.name == 'adHocJobStdout') { if ($state.current.name == 'adHocJobStdout') {
$log.debug("socket watching on ad_hoc_command_events-" + job_id); $log.debug("socket watching on ad_hoc_command_events-" + job_id);
$rootScope.adhoc_event_socket.on("ad_hoc_command_events-" + job_id, function() { $rootScope.adhoc_event_socket.on("ad_hoc_command_events-" + job_id, function() {
@@ -108,7 +117,7 @@ export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'Proce
function getNextSection() { function getNextSection() {
// get the next range of data from the API // get the next range of data from the API
var start = loaded_sections[loaded_sections.length - 1].end, url; var start = loaded_sections[loaded_sections.length - 1].end, url;
url = stdout_url + '?format=json&start_line=' + start + '&end_line=' + (start + page_size); url = $scope.stdoutEndpoint + '?format=json&start_line=' + start + '&end_line=' + (start + page_size);
$('#stdoutMoreRowsBottom').fadeIn(); $('#stdoutMoreRowsBottom').fadeIn();
Rest.setUrl(url); Rest.setUrl(url);
Rest.get() Rest.get()