mirror of
https://github.com/ansible/awx.git
synced 2026-01-16 12:20:45 -03:30
Merge pull request #5594 from jaredevantabor/decouple-jobDetails-jobResults
RIP Old Job Details Page
This commit is contained in:
commit
c839bb67d0
@ -1671,7 +1671,7 @@ tr td button i {
|
||||
.modal-body {
|
||||
min-height: 120px;
|
||||
padding: 20px 0;
|
||||
|
||||
|
||||
.alert {
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
@ -1984,10 +1984,6 @@ tr td button i {
|
||||
width: 73px;
|
||||
}
|
||||
|
||||
.JobDetails-status {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.red-text {
|
||||
color: @red;
|
||||
}
|
||||
|
||||
@ -46,7 +46,6 @@ import inventories from './inventories/main';
|
||||
import inventoryScripts from './inventory-scripts/main';
|
||||
import organizations from './organizations/main';
|
||||
import managementJobs from './management-jobs/main';
|
||||
import jobDetail from './job-detail/main';
|
||||
import workflowResults from './workflow-results/main';
|
||||
import jobResults from './job-results/main';
|
||||
import jobSubmission from './job-submission/main';
|
||||
@ -119,7 +118,6 @@ var tower = angular.module('Tower', [
|
||||
login.name,
|
||||
activityStream.name,
|
||||
footer.name,
|
||||
jobDetail.name,
|
||||
workflowResults.name,
|
||||
jobResults.name,
|
||||
jobSubmission.name,
|
||||
@ -191,7 +189,6 @@ var tower = angular.module('Tower', [
|
||||
'LogViewerStatusDefinition',
|
||||
'StandardOutHelper',
|
||||
'LogViewerOptionsDefinition',
|
||||
'JobDetailHelper',
|
||||
'lrInfiniteScroll',
|
||||
'LoadConfigHelper',
|
||||
'PortalJobsListDefinition',
|
||||
@ -350,19 +347,19 @@ var tower = angular.module('Tower', [
|
||||
|
||||
$rootScope.$on("$stateChangeStart", function (event, next) {
|
||||
// Remove any lingering intervals
|
||||
// except on jobDetails.* states
|
||||
var jobDetailStates = [
|
||||
'jobDetail',
|
||||
'jobDetail.host-summary',
|
||||
'jobDetail.host-event.details',
|
||||
'jobDetail.host-event.json',
|
||||
'jobDetail.host-events',
|
||||
'jobDetail.host-event.stdout'
|
||||
// except on jobResults.* states
|
||||
var jobResultStates = [
|
||||
'jobResult',
|
||||
'jobResult.host-summary',
|
||||
'jobResult.host-event.details',
|
||||
'jobResult.host-event.json',
|
||||
'jobResult.host-events',
|
||||
'jobResult.host-event.stdout'
|
||||
];
|
||||
if ($rootScope.jobDetailInterval && !_.includes(jobDetailStates, next.name) ) {
|
||||
window.clearInterval($rootScope.jobDetailInterval);
|
||||
if ($rootScope.jobResultInterval && !_.includes(jobResultStates, next.name) ) {
|
||||
window.clearInterval($rootScope.jobResultInterval);
|
||||
}
|
||||
if ($rootScope.jobStdOutInterval && !_.includes(jobDetailStates, next.name) ) {
|
||||
if ($rootScope.jobStdOutInterval && !_.includes(jobResultStates, next.name) ) {
|
||||
window.clearInterval($rootScope.jobStdOutInterval);
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,6 @@ import Credentials from "./helpers/Credentials";
|
||||
import Events from "./helpers/Events";
|
||||
import Groups from "./helpers/Groups";
|
||||
import Hosts from "./helpers/Hosts";
|
||||
import JobDetail from "./helpers/JobDetail";
|
||||
import JobSubmission from "./helpers/JobSubmission";
|
||||
import JobTemplates from "./helpers/JobTemplates";
|
||||
import Jobs from "./helpers/Jobs";
|
||||
@ -38,7 +37,6 @@ export
|
||||
Events,
|
||||
Groups,
|
||||
Hosts,
|
||||
JobDetail,
|
||||
JobSubmission,
|
||||
JobTemplates,
|
||||
Jobs,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,2 +0,0 @@
|
||||
<textarea id="HostEvent-codemirror" class="HostEvent-codemirror">
|
||||
</textarea>
|
||||
@ -1,45 +0,0 @@
|
||||
<div class="HostEvent-details--left">
|
||||
<div class="HostEvent-title">EVENT</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">HOST</span>
|
||||
<span class="HostEvent-field--content">
|
||||
<a ui-sref="jobDetail.host-events({hostName: event.host_name})">{{event.host_name || "No result found"}}</a></span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">STATUS</span>
|
||||
<span class="HostEvent-field--content">
|
||||
<a class="HostEvents-status">
|
||||
<i class="fa fa-circle" ng-class="processEventStatus(event).class"></i>
|
||||
</a>
|
||||
{{processEventStatus(event).status || "No result found"}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">ID</span>
|
||||
<span class="HostEvent-field--content">{{event.id || "No result found"}}</span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">CREATED</span>
|
||||
<span class="HostEvent-field--content">{{(event.created | longDate) || "No result found"}}</span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">PLAY</span>
|
||||
<span class="HostEvent-field--content">{{event.play || "No result found"}}</span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">TASK</span>
|
||||
<span class="HostEvent-field--content">{{event.task || "No result found"}}</span>
|
||||
</div>
|
||||
<div class="HostEvent-field">
|
||||
<span class="HostEvent-field--label">MODULE</span>
|
||||
<span class="HostEvent-field--content">{{event.event_data.res.invocation.module_name || "No result found"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="HostEvent-details--right" ng-show="event.event_data.res">
|
||||
<div class="HostEvent-title">RESULTS</div>
|
||||
<!-- discard any objects in the ansible response until we decide to flatten them -->
|
||||
<div class="HostEvent-field" ng-repeat="(key, value) in results = event.event_data.res track by $index" ng-if="processResults(value)">
|
||||
<span class="HostEvent-field--label">{{key}}</span>
|
||||
<span class="HostEvent-field--content">{{value}}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,36 +0,0 @@
|
||||
<div id="HostEvent" class="HostEvent modal" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<!-- modal body -->
|
||||
<div class="modal-body">
|
||||
<div class="HostEvent-header">
|
||||
<span class="HostEvent-title">HOST EVENT</span>
|
||||
<!-- close -->
|
||||
<button ui-sref="jobDetail" type="button" class="close">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="HostEvent-nav">
|
||||
<!-- view navigation buttons -->
|
||||
<button ui-sref="jobDetail.host-event.details" type="button" class="btn btn-sm btn-default HostEvent-tab" ng-class="{'HostEvent-tab--selected' : isActiveState('jobDetail.host-event.details')}">Details</button>
|
||||
<button ui-sref="jobDetail.host-event.json" type="button" class="btn btn-sm btn-default HostEvent-tab" ng-class="{'HostEvent-tab--selected' : isActiveState('jobDetail.host-event.json')}">JSON</button>
|
||||
<button ng-if="stdout" ui-sref="jobDetail.host-event.stdout" type="button" class="btn btn-sm btn-default HostEvent-tab" ng-class="{'HostEvent-tab--selected' : isActiveState('jobDetail.host-event.stdout')}">Standard Out</button>
|
||||
<button ng-if="stderr" ui-sref="jobDetail.host-event.stderr" type="button" class="btn btn-sm btn-default HostEvent-tab" ng-class="{'HostEvent-tab--selected' : isActiveState('jobDetail.host-event.stderr')}">Standard Error</button>
|
||||
|
||||
</div>
|
||||
<div class="HostEvent-body">
|
||||
<!-- views -->
|
||||
<div class="HostEvent-view--container" ui-view></div>
|
||||
</div>
|
||||
|
||||
<!-- controls -->
|
||||
<div class="HostEvent-controls">
|
||||
<button ng-disabled="!showPrev()" ng-click="goPrev()"
|
||||
class="btn btn-sm btn-default HostEvent-button">Prev Host</button>
|
||||
<button ng-disabled="!showNext()"ng-click="goNext()" class="btn btn-sm btn-default HostEvent-button">Next Host</button>
|
||||
<button ui-sref="jobDetail" class="btn btn-sm btn-default HostEvent-close HostEvent-button" ng-show="true" >Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1 +0,0 @@
|
||||
<div>timing</div>
|
||||
@ -1,150 +0,0 @@
|
||||
// @import "./client/src/shared/branding/colors.less";
|
||||
// @import "./client/src/shared/branding/colors.default.less";
|
||||
// @import "./client/src/shared/layouts/one-plus-two.less";
|
||||
//
|
||||
// .noselect {
|
||||
// -webkit-touch-callout: none; /* iOS Safari */
|
||||
// -webkit-user-select: none; /* Chrome/Safari/Opera */
|
||||
// -khtml-user-select: none; /* Konqueror */
|
||||
// -moz-user-select: none; /* Firefox */
|
||||
// -ms-user-select: none; /* Internet Explorer/Edge */
|
||||
// user-select: none; /* Non-prefixed version, currently
|
||||
// not supported by any browser */
|
||||
// }
|
||||
//
|
||||
// @media screen and (min-width: 768px){
|
||||
// .HostEvent .modal-dialog{
|
||||
// width: 700px;
|
||||
// }
|
||||
// }
|
||||
// .HostEvent .CodeMirror{
|
||||
// overflow-x: hidden;
|
||||
// }
|
||||
// .HostEvent-controls button.HostEvent-close{
|
||||
// color: #FFFFFF;
|
||||
// text-transform: uppercase;
|
||||
// padding-left: 15px;
|
||||
// padding-right: 15px;
|
||||
// background-color: @default-link;
|
||||
// border-color: @default-link;
|
||||
// &:hover{
|
||||
// background-color: @default-link-hov;
|
||||
// border-color: @default-link-hov;
|
||||
// }
|
||||
// }
|
||||
// .HostEvent-body{
|
||||
// margin-bottom: 10px;
|
||||
// }
|
||||
// .HostEvent-tab {
|
||||
// color: @btn-txt;
|
||||
// background-color: @btn-bg;
|
||||
// font-size: 12px;
|
||||
// border: 1px solid @btn-bord;
|
||||
// height: 30px;
|
||||
// border-radius: 5px;
|
||||
// margin-right: 20px;
|
||||
// padding-left: 10px;
|
||||
// padding-right: 10px;
|
||||
// padding-bottom: 5px;
|
||||
// padding-top: 5px;
|
||||
// transition: background-color 0.2s;
|
||||
// text-transform: uppercase;
|
||||
// text-align: center;
|
||||
// white-space: nowrap;
|
||||
// .noselect;
|
||||
// }
|
||||
// .HostEvent-tab:hover {
|
||||
// color: @btn-txt;
|
||||
// background-color: @btn-bg-hov;
|
||||
// cursor: pointer;
|
||||
// }
|
||||
// .HostEvent-tab--selected{
|
||||
// color: @btn-txt-sel!important;
|
||||
// background-color: @default-icon!important;
|
||||
// border-color: @default-icon!important;
|
||||
// }
|
||||
// .HostEvent-view--container{
|
||||
// width: 100%;
|
||||
// display: flex;
|
||||
// flex-direction: row;
|
||||
// flex-wrap: nowrap;
|
||||
// justify-content: space-between;
|
||||
// }
|
||||
// .HostEvent .modal-footer{
|
||||
// border: 0;
|
||||
// margin-top: 0px;
|
||||
// padding-top: 5px;
|
||||
// }
|
||||
// .HostEvent-controls{
|
||||
// float: right;
|
||||
// button {
|
||||
// margin-left: 10px;
|
||||
// }
|
||||
// }
|
||||
// .HostEvent-status--ok{
|
||||
// color: @green;
|
||||
// }
|
||||
// .HostEvent-status--unreachable{
|
||||
// color: @unreachable;
|
||||
// }
|
||||
// .HostEvent-status--changed{
|
||||
// color: @changed;
|
||||
// }
|
||||
// .HostEvent-status--failed{
|
||||
// color: @default-err;
|
||||
// }
|
||||
// .HostEvent-status--skipped{
|
||||
// color: @skipped;
|
||||
// }
|
||||
// .HostEvent-title{
|
||||
// color: @default-interface-txt;
|
||||
// font-weight: 600;
|
||||
// margin-bottom: 8px;
|
||||
// }
|
||||
// // .HostEvent .modal-body{
|
||||
// // max-height: 500px;
|
||||
// // overflow-y: auto;
|
||||
// // padding: 20px;
|
||||
// // }
|
||||
// .HostEvent-nav{
|
||||
// padding-top: 12px;
|
||||
// padding-bottom: 12px;
|
||||
// }
|
||||
// .HostEvent-field{
|
||||
// margin-bottom: 8px;
|
||||
// flex: 0 1 12em;
|
||||
// }
|
||||
// .HostEvent-field--label{
|
||||
// text-transform: uppercase;
|
||||
// flex: 0 1 80px;
|
||||
// max-width: 80px;
|
||||
// font-size: 12px;
|
||||
// word-wrap: break-word;
|
||||
// }
|
||||
// .HostEvent-field{
|
||||
// .OnePlusTwo-left--detailsRow;
|
||||
// }
|
||||
// .HostEvent-field--content{
|
||||
// word-wrap: break-word;
|
||||
// max-width: 13em;
|
||||
// flex: 0 1 13em;
|
||||
// }
|
||||
// .HostEvent-details--left, .HostEvent-details--right{
|
||||
// flex: 1 1 47%;
|
||||
// }
|
||||
// .HostEvent-details--left{
|
||||
// margin-right: 40px;
|
||||
// }
|
||||
// .HostEvent-details--right{
|
||||
// .HostEvent-field--label{
|
||||
// flex: 0 1 25em;
|
||||
// }
|
||||
// .HostEvent-field--content{
|
||||
// max-width: 15em;
|
||||
// flex: 0 1 15em;
|
||||
// align-self: flex-end;
|
||||
// }
|
||||
// }
|
||||
// .HostEvent-button:disabled {
|
||||
// pointer-events: all!important;
|
||||
// }
|
||||
@ -1,110 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
|
||||
export default
|
||||
['$stateParams', '$scope', '$state', 'Wait', 'JobDetailService', 'hostEvent', 'hostResults',
|
||||
function($stateParams, $scope, $state, Wait, JobDetailService, hostEvent, hostResults){
|
||||
|
||||
$scope.processEventStatus = JobDetailService.processEventStatus;
|
||||
$scope.hostResults = [];
|
||||
// Avoid rendering objects in the details fieldset
|
||||
// ng-if="processResults(value)" via host-event-details.partial.html
|
||||
$scope.processResults = function(value){
|
||||
if (typeof value === 'object'){return false;}
|
||||
else {return true;}
|
||||
};
|
||||
$scope.isStdOut = function(){
|
||||
if ($state.current.name === 'jobDetails.host-event.stdout' || $state.current.name === 'jobDetaisl.histe-event.stderr'){
|
||||
return 'StandardOut-preContainer StandardOut-preContent';
|
||||
}
|
||||
};
|
||||
/*ignore jslint start*/
|
||||
var initCodeMirror = function(el, data, mode){
|
||||
var container = document.getElementById(el);
|
||||
var editor = CodeMirror.fromTextArea(container, { // jshint ignore:line
|
||||
lineNumbers: true,
|
||||
mode: mode
|
||||
});
|
||||
editor.setSize("100%", 300);
|
||||
editor.getDoc().setValue(data);
|
||||
};
|
||||
/*ignore jslint end*/
|
||||
$scope.isActiveState = function(name){
|
||||
return $state.current.name === name;
|
||||
};
|
||||
|
||||
$scope.getActiveHostIndex = function(){
|
||||
var result = $scope.hostResults.filter(function( obj ) {
|
||||
return obj.id === $scope.event.id;
|
||||
});
|
||||
return $scope.hostResults.indexOf(result[0]);
|
||||
};
|
||||
|
||||
$scope.showPrev = function(){
|
||||
return $scope.getActiveHostIndex() !== 0;
|
||||
};
|
||||
|
||||
$scope.showNext = function(){
|
||||
return $scope.getActiveHostIndex() < $scope.hostResults.indexOf($scope.hostResults[$scope.hostResults.length - 1]);
|
||||
};
|
||||
|
||||
$scope.goNext = function(){
|
||||
var index = $scope.getActiveHostIndex() + 1;
|
||||
var id = $scope.hostResults[index].id;
|
||||
$state.go('jobDetail.host-event.details', {eventId: id});
|
||||
};
|
||||
|
||||
$scope.goPrev = function(){
|
||||
var index = $scope.getActiveHostIndex() - 1;
|
||||
var id = $scope.hostResults[index].id;
|
||||
$state.go('jobDetail.host-event.details', {eventId: id});
|
||||
};
|
||||
|
||||
var init = function(){
|
||||
$scope.event = _.cloneDeep(hostEvent);
|
||||
$scope.hostResults = hostResults;
|
||||
$scope.json = JobDetailService.processJson(hostEvent);
|
||||
|
||||
// grab standard out & standard error if present, and remove from the results displayed in the details panel
|
||||
if (hostEvent.event_data.res.stdout){
|
||||
$scope.stdout = hostEvent.event_data.res.stdout;
|
||||
delete $scope.event.event_data.res.stdout;
|
||||
}
|
||||
if (hostEvent.event_data.res.stderr){
|
||||
$scope.stderr = hostEvent.event_data.res.stderr;
|
||||
delete $scope.event.event_data.res.stderr;
|
||||
}
|
||||
// instantiate Codemirror
|
||||
// try/catch pattern prevents the abstract-state controller from complaining about element being null
|
||||
if ($state.current.name === 'jobDetail.host-event.json'){
|
||||
try{
|
||||
initCodeMirror('HostEvent-codemirror', JSON.stringify($scope.json, null, 4), {name: "javascript", json: true});
|
||||
}
|
||||
catch(err){
|
||||
// element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController
|
||||
}
|
||||
}
|
||||
else if ($state.current.name === 'jobDetail.host-event.stdout'){
|
||||
try{
|
||||
initCodeMirror('HostEvent-codemirror', $scope.stdout, 'shell');
|
||||
}
|
||||
catch(err){
|
||||
// element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController
|
||||
}
|
||||
}
|
||||
else if ($state.current.name === 'jobDetail.host-event.stderr'){
|
||||
try{
|
||||
initCodeMirror('HostEvent-codemirror', $scope.stderr, 'shell');
|
||||
}
|
||||
catch(err){
|
||||
// element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController
|
||||
}
|
||||
}
|
||||
$('#HostEvent').modal('show');
|
||||
};
|
||||
init();
|
||||
}];
|
||||
@ -1,65 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import { templateUrl } from '../../shared/template-url/template-url.factory';
|
||||
|
||||
var hostEventModal = {
|
||||
name: 'jobDetail.host-event',
|
||||
url: '/task/:taskId/host-event/:eventId',
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-detail/host-event/host-event-modal'),
|
||||
'abstract': true,
|
||||
resolve: {
|
||||
hostEvent: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) {
|
||||
return JobDetailService.getRelatedJobEvents($stateParams.id, {
|
||||
id: $stateParams.eventId
|
||||
}).then(function(res) {
|
||||
return res.data.results[0]; });
|
||||
}],
|
||||
hostResults: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) {
|
||||
return JobDetailService.getJobEventChildren($stateParams.taskUuid).then(res => res.data.results);
|
||||
}]
|
||||
},
|
||||
onExit: function() {
|
||||
// close the modal
|
||||
// using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X"
|
||||
$('#HostEvent').modal('hide');
|
||||
// hacky way to handle user browsing away via URL bar
|
||||
$('.modal-backdrop').remove();
|
||||
$('body').removeClass('modal-open');
|
||||
}
|
||||
};
|
||||
|
||||
var hostEventDetails = {
|
||||
name: 'jobDetail.host-event.details',
|
||||
url: '/details',
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-detail/host-event/host-event-details'),
|
||||
};
|
||||
|
||||
var hostEventJson = {
|
||||
name: 'jobDetail.host-event.json',
|
||||
url: '/json',
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-detail/host-event/host-event-codemirror')
|
||||
};
|
||||
|
||||
var hostEventStdout = {
|
||||
name: 'jobDetail.host-event.stdout',
|
||||
url: '/stdout',
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-detail/host-event/host-event-codemirror')
|
||||
};
|
||||
|
||||
var hostEventStderr = {
|
||||
name: 'jobDetail.host-event.stderr',
|
||||
url: '/stderr',
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-detail/host-event/host-event-codemirror')
|
||||
};
|
||||
|
||||
|
||||
export { hostEventDetails, hostEventJson, hostEventModal, hostEventStdout, hostEventStderr };
|
||||
@ -1,21 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {hostEventModal, hostEventDetails,
|
||||
hostEventJson, hostEventStdout, hostEventStderr} from './host-event.route';
|
||||
import controller from './host-event.controller';
|
||||
|
||||
export default
|
||||
angular.module('jobDetail.hostEvent', [])
|
||||
.controller('HostEventController', controller)
|
||||
|
||||
.run(['$stateExtender', function($stateExtender){
|
||||
$stateExtender.addState(hostEventModal);
|
||||
$stateExtender.addState(hostEventDetails);
|
||||
$stateExtender.addState(hostEventJson);
|
||||
$stateExtender.addState(hostEventStdout);
|
||||
$stateExtender.addState(hostEventStderr);
|
||||
}]);
|
||||
@ -1,93 +0,0 @@
|
||||
@import "./client/src/shared/branding/colors.less";
|
||||
@import "./client/src/shared/branding/colors.default.less";
|
||||
|
||||
.HostEvents .CodeMirror{
|
||||
border: none;
|
||||
}
|
||||
.HostEvents .modal-footer{
|
||||
border: 0;
|
||||
margin-top: 0px;
|
||||
padding: 0px 20px 20px 20px;
|
||||
}
|
||||
button.HostEvents-close{
|
||||
width: 70px;
|
||||
color: #FFFFFF!important;
|
||||
text-transform: uppercase;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
background-color: @default-link;
|
||||
border-color: @default-link;
|
||||
&:hover{
|
||||
background-color: @default-link-hov;
|
||||
border-color: @default-link-hov;
|
||||
}
|
||||
}
|
||||
.HostEvents-status--ok{
|
||||
color: @green;
|
||||
}
|
||||
.HostEvents-status--unreachable{
|
||||
color: @unreachable;
|
||||
}
|
||||
.HostEvents-status--changed{
|
||||
color: @changed;
|
||||
}
|
||||
.HostEvents-status--failed{
|
||||
color: @default-err;
|
||||
}
|
||||
.HostEvents-status--skipped{
|
||||
color: @skipped;
|
||||
}
|
||||
|
||||
.HostEvents-filter--form{
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
float: right;
|
||||
display: inline-block;
|
||||
}
|
||||
.HostEvents .modal-body{
|
||||
padding: 20px;
|
||||
}
|
||||
.HostEvents .select2-container{
|
||||
text-transform: capitalize;
|
||||
max-width: 220px;
|
||||
float: right;
|
||||
}
|
||||
.HostEvents-form--container{
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.HostEvents-title{
|
||||
text-transform: uppercase;
|
||||
color: @default-interface-txt;
|
||||
font-weight: 600;
|
||||
}
|
||||
.HostEvents-status i {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.HostEvents-table--header {
|
||||
height: 30px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
color: @default-interface-txt;
|
||||
background-color: @default-list-header-bg;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
.HostEvents-table--header:first-of-type{
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
.HostEvents-table--header:last-of-type{
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
.HostEvents-table--row{
|
||||
color: @default-data-txt;
|
||||
border: 0 !important;
|
||||
}
|
||||
.HostEvents-table--row:nth-child(odd){
|
||||
background: @default-tertiary-bg;
|
||||
}
|
||||
.HostEvents-table--cell{
|
||||
border: 0 !important;
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$stateParams', '$scope', '$rootScope', '$state', 'Wait',
|
||||
'JobDetailService', 'CreateSelect2', 'hosts',
|
||||
function($stateParams, $scope, $rootScope, $state, Wait,
|
||||
JobDetailService, CreateSelect2, hosts){
|
||||
|
||||
// pagination not implemented yet, but it'll depend on this
|
||||
$scope.page_size = $stateParams.page_size;
|
||||
|
||||
$scope.processEventStatus = JobDetailService.processEventStatus;
|
||||
$scope.activeFilter = $stateParams.filter || null;
|
||||
|
||||
$scope.filters = ['all', 'changed', 'failed', 'ok', 'unreachable', 'skipped'];
|
||||
|
||||
// watch select2 for changes
|
||||
$('.HostEvents-select').on("select2:select", function () {
|
||||
$scope.activeFilter = $('.HostEvents-select').val();
|
||||
});
|
||||
|
||||
var init = function(){
|
||||
$scope.hostName = $stateParams.hostName;
|
||||
// create filter dropdown
|
||||
CreateSelect2({
|
||||
element: '.HostEvents-select',
|
||||
multiple: false
|
||||
});
|
||||
// process the filter if one was passed
|
||||
if ($stateParams.filter){
|
||||
$scope.activeFilter = $stateParams.filter;
|
||||
|
||||
$('#HostEvents').modal('show');
|
||||
}
|
||||
else{
|
||||
$scope.results = hosts.data.results;
|
||||
$('#HostEvents').modal('show');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
init();
|
||||
|
||||
}];
|
||||
@ -1,52 +0,0 @@
|
||||
<div id="HostEvents" class="HostEvents modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="HostEvents-header">
|
||||
<span class="HostEvents-title">HOST EVENTS | {{hostName}}</span>
|
||||
<!-- Close -->
|
||||
<button ui-sref="jobDetail.host-summary" type="button" class="close">
|
||||
<i class="fa fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="HostEvents-form--container">
|
||||
<select class="HostEvents-select">
|
||||
<option ng-selected="filter == activeFilter" class="HostEvents-select--option" value="{{filter}}" ng-repeat="filter in filters">{{filter}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- event results table -->
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<!-- column labels -->
|
||||
<th ng-hide="results.length == 0" class="HostEvents-table--header">STATUS</th>
|
||||
<th ng-hide="results.length == 0" class="HostEvents-table--header">PLAY</th>
|
||||
<th ng-hide="results.length == 0" class="HostEvents-table--header">TASK</th>
|
||||
<!-- result rows -->
|
||||
<tr class="HostEvents-table--row" ng-repeat="event in results track by $index" modal-paginate="event in results | page_size: page_size">
|
||||
<td class=HostEvents-table--cell>
|
||||
<!-- status circles -->
|
||||
<a class="HostEvents-status">
|
||||
<i class="fa fa-circle" ng-class="processEventStatus(event).class"></i>
|
||||
</a>
|
||||
{{processEventStatus(event).status}}
|
||||
</td>
|
||||
<td class=HostEvents-table--cell>{{event.play}}</td>
|
||||
<td class=HostEvents-table--cell>{{event.task}}</td>
|
||||
</tr>
|
||||
<tr ng-show="results.length == 0" class="HostEvents-table--row">
|
||||
<td class=HostEvents-table--cell>
|
||||
No results were found.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<!-- pagination -->
|
||||
<!-- close -->
|
||||
<button ui-sref="jobDetail.host-summary" class="btn btn-default pull-right HostEvents-close">OK</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,32 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'jobDetail.host-events',
|
||||
url: '/host-events/{hostName:any}?:filter',
|
||||
controller: 'HostEventsController',
|
||||
params: {
|
||||
page_size: 10
|
||||
},
|
||||
templateUrl: templateUrl('job-detail/host-events/host-events'),
|
||||
onExit: function(){
|
||||
// close the modal
|
||||
// using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X"
|
||||
$('#HostEvents').modal('hide');
|
||||
// hacky way to handle user browsing away via URL bar
|
||||
$('.modal-backdrop').remove();
|
||||
$('body').removeClass('modal-open');
|
||||
},
|
||||
resolve: {
|
||||
hosts: ['JobDetailService','$stateParams', function(JobDetailService, $stateParams) {
|
||||
return JobDetailService.getRelatedJobEvents($stateParams.id, {
|
||||
host_name: $stateParams.hostName
|
||||
}).success(function(res){ return res.results[0];});
|
||||
}]
|
||||
}
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './host-events.route';
|
||||
import controller from './host-events.controller';
|
||||
|
||||
export default
|
||||
angular.module('jobDetail.hostEvents', [])
|
||||
.controller('HostEventsController', controller)
|
||||
.run(['$stateExtender', function($stateExtender){
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
@ -1,17 +0,0 @@
|
||||
@import '../../shared/branding/colors.default.less';
|
||||
.HostSummary-graph--successful{
|
||||
text-anchor: start !important;
|
||||
}
|
||||
.HostSummary-graph--failed{
|
||||
text-anchor: end !important;
|
||||
}
|
||||
.HostSummary-graph--changed{
|
||||
text-anchor: start !important;
|
||||
}
|
||||
.HostSummary-loading{
|
||||
border: none;
|
||||
}
|
||||
.HostSummary-loading{
|
||||
padding-left: 0px !important;
|
||||
color: @default-interface-txt;
|
||||
}
|
||||
@ -1,131 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$scope', '$rootScope', '$stateParams', 'Wait', 'JobDetailService', 'DrawGraph', function($scope, $rootScope, $stateParams, Wait, JobDetailService, DrawGraph){
|
||||
|
||||
var page_size = 200;
|
||||
$scope.loading = $scope.hosts.length > 0 ? false : true;
|
||||
$scope.filter = 'all';
|
||||
|
||||
var buildGraph = function(hosts){
|
||||
// status waterfall: unreachable > failed > changed > ok > skipped
|
||||
var count;
|
||||
count = {
|
||||
ok : _.filter(hosts, function(o){
|
||||
return o.failures === 0 && o.changed === 0 && o.ok > 0;
|
||||
}),
|
||||
skipped : _.filter(hosts, function(o){
|
||||
return o.skipped > 0;
|
||||
}),
|
||||
unreachable : _.filter(hosts, function(o){
|
||||
return o.dark > 0;
|
||||
}),
|
||||
failures : _.filter(hosts, function(o){
|
||||
return o.failed === true;
|
||||
}),
|
||||
changed : _.filter(hosts, function(o){
|
||||
return o.changed > 0;
|
||||
})
|
||||
};
|
||||
return count;
|
||||
};
|
||||
var init = function(){
|
||||
Wait('start');
|
||||
JobDetailService.getJobHostSummaries($stateParams.id, {page_size: page_size, order_by: 'host_name'})
|
||||
.success(function(res){
|
||||
$scope.hosts = res.results;
|
||||
$scope.next = res.next;
|
||||
$scope.count = buildGraph(res.results);
|
||||
Wait('stop');
|
||||
DrawGraph({count: $scope.count, resize:true});
|
||||
});
|
||||
JobDetailService.getJob({id: $stateParams.id})
|
||||
.success(function(res){
|
||||
$scope.status = res.results[0].status;
|
||||
});
|
||||
};
|
||||
if ($rootScope.removeJobSummaryComplete) {
|
||||
$rootScope.removeJobSummaryComplete();
|
||||
}
|
||||
// emitted by the API in the same function used to persist host summary data
|
||||
// JobEvent.update_host_summary_from_stats() from /awx/main.models.jobs.py
|
||||
$scope.$on('ws-jobs-summary', function(e, data) {
|
||||
// discard socket msgs we don't care about in this context
|
||||
if (parseInt($stateParams.id) === data.unified_job_id){
|
||||
init();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('ws-jobs', function(e, data) {
|
||||
if (parseInt($stateParams.id) === data.unified_job_id){
|
||||
$scope.status = data.status;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$scope.buildTooltip = function(n, status){
|
||||
var grammar = function(n, status){
|
||||
var dict = {
|
||||
0: 'No host events were ',
|
||||
1: ' host event was ',
|
||||
2: ' host events were '
|
||||
};
|
||||
if (n >= 2){
|
||||
return n + dict[2] + status;
|
||||
}
|
||||
else{
|
||||
return n !== 0 ? n + dict[n] + status : dict[n] + status;
|
||||
}
|
||||
};
|
||||
return grammar(n, status);
|
||||
};
|
||||
$scope.getNextPage = function(){
|
||||
if ($scope.next){
|
||||
JobDetailService.getNextPage($scope.next).success(function(res){
|
||||
res.results.forEach(function(key, index){
|
||||
$scope.hosts.push(res.results[index]);
|
||||
});
|
||||
$scope.hosts.push(res.results);
|
||||
$scope.next = res.next;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setFilter = function(filter){
|
||||
$scope.filter = filter;
|
||||
var getAll = function(){
|
||||
Wait('start');
|
||||
JobDetailService.getJobHostSummaries($stateParams.id, {
|
||||
page_size: page_size,
|
||||
order_by: 'host_name'
|
||||
}).success(function(res){
|
||||
Wait('stop');
|
||||
$scope.hosts = res.results;
|
||||
$scope.next = res.next;
|
||||
});
|
||||
};
|
||||
var getFailed = function(){
|
||||
Wait('start');
|
||||
JobDetailService.getJobHostSummaries($stateParams.id, {
|
||||
page_size: page_size,
|
||||
failed: true,
|
||||
order_by: 'host_name'
|
||||
}).success(function(res){
|
||||
Wait('stop');
|
||||
$scope.hosts = res.results;
|
||||
$scope.next = res.next;
|
||||
});
|
||||
};
|
||||
$scope.get = filter === 'all' ? getAll() : getFailed();
|
||||
};
|
||||
|
||||
init();
|
||||
// calling the init routine twice will size the d3 chart correctly - no idea why
|
||||
// instantiating the graph inside a setTimeout() SHOULD have the same effect, but it doesn't
|
||||
// instantiating the graph further down the promise chain e.g. .then() or .finally() also does not work
|
||||
init();
|
||||
}];
|
||||
@ -1,72 +0,0 @@
|
||||
|
||||
<div id="hosts-summary-section" class="section">
|
||||
<div class="JobDetail-instructions" ng-hide="hosts.length == 0 && !searchTerm && !searchActive"><span class="badge">4</span> Please select a host below to view a summary of all associated tasks.</div>
|
||||
<div class="JobDetail-searchHeaderRow" ng-hide="hosts.length == 0 && !searchTerm && !searchActive">
|
||||
<div class="JobDetail-searchContainer form-group">
|
||||
<div class="search-name">
|
||||
<form ng-submit="search()">
|
||||
<input type="text" class="JobDetail-searchInput form-control List-searchInput" id="search_host_summary_name" ng-model="searchTerm" placeholder="Host Name" />
|
||||
<a class="List-searchInputIcon search-icon" ng-click="search()" ng-show="!searchActive"><i class="fa fa-search"></i></a>
|
||||
<a class="List-searchInputIcon search-icon" ng-show="searchActive" ng-click="clearSearch()"><i class="fa fa-times"></i></a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="JobDetail-tableToggleContainer form-group">
|
||||
<div class="btn-group" >
|
||||
<button
|
||||
ng-click="setFilter('all')"
|
||||
class="JobDetail-tableToggle btn btn-xs" ng-class="{'btn-default': filter === 'failed', 'btn-primary': filter === 'all'}">All</button>
|
||||
<button ng-click="setFilter('failed')"
|
||||
ng-class="{'btn-default': filter === 'all', 'btn-primary': filter === 'failed'}" ng-disabled='count.failures == 0' class="JobDetail-tableToggle btn btn-xs">Failed</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-header" ng-hide="hosts.length == 0 && !searchTerm && !searchActive">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="List-tableHeader col-lg-6 col-md-6 col-sm-6 col-xs-6">Hosts</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-6 col-md-5 col-sm-5 col-xs-5">Completed Tasks</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="hosts-summary-table" class="table-detail" lr-infinite-scroll="getNextPage" scroll-threshold="10" time-threshold="500">
|
||||
<table class="table" ng-class="{'JobDetails-table--noResults': hosts.length === 0}">
|
||||
<tbody>
|
||||
<tr class="List-tableRow" ng-repeat="host in 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 ui-sref="jobDetail.host-events({hostName: host.host_name})" aw-tool-tip="View events" data-placement="top">{{ host.host_name }}</a>
|
||||
</td>
|
||||
<td class="List-tableCell col-lg-6 col-md-5 col-sm-5 col-xs-5 badge-column">
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'ok'})" aw-tool-tip="{{ buildTooltip(host.ok - host.changed, 'ok') }}" data-placement="top" ng-hide="host.ok == 0"><span class="badge successful-hosts">{{ host.ok - host.changed }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'changed'})" aw-tool-tip="{{buildTooltip(host.changed, 'changed')}}" data-placement="top" ng-hide="host.changed == 0"><span class="badge changed-hosts">{{ host.changed }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'skipped'})" aw-tool-tip="{{buildTooltip(host.skipped, 'skipped')}}" data-placement="top" ng-hide="host.skipped == 0"><span class="badge skipped-hosts">{{ host.skipped }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'unreachable'})" aw-tool-tip="{{buildTooltip(host.dark, 'unreachable')}}" data-placement="top" ng-hide="host.dark == 0"><span class="badge unreachable-hosts">{{ host.dark }}</span></a>
|
||||
<a ui-sref="jobDetail.host-events({hostName: host.host_name, filter: 'failed'})" aw-tool-tip="{{ buildTooltip(host.failures, 'failed')}}" data-placement="top" ng-hide="host.failed == 0"><span class="badge failed-hosts">{{ host.failures }}</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="status == 'pending'">
|
||||
<td colspan="5" class="col-lg-12 HostSummary-loading">Initiating job run.</td>
|
||||
</tr>
|
||||
<tr ng-show="status == 'running'">
|
||||
<td colspan="5" class="col-lg-12 HostSummary-loading">Job is running. Summary will be available on completion.</td>
|
||||
</tr>
|
||||
<tr ng-show="(status === 'failed' || status === 'successful') && hosts.length === 0 ">
|
||||
<td colspan="2" class="col-lg-12 HostSummary-loading">No matching hosts</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="scroll-spinner" id="hostSummariesMoreRows">
|
||||
<i class="fa fa-cog fa-spin"></i>
|
||||
</div>
|
||||
|
||||
</div><!-- section -->
|
||||
<div class="JobDetail-panelHeaderText">Host Status Summary</div>
|
||||
<div id="graph-section" class="JobDetail-graphSection">
|
||||
<svg></svg>
|
||||
</div>
|
||||
@ -1,21 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'jobDetail.host-summary',
|
||||
url: '/event-summary',
|
||||
views:{
|
||||
'host-summary': {
|
||||
controller: 'HostSummaryController',
|
||||
templateUrl: templateUrl('job-detail/host-summary/host-summary'),
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
skip: true // Never display this state in breadcrumb.
|
||||
}
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './host-summary.route';
|
||||
import controller from './host-summary.controller';
|
||||
|
||||
export default
|
||||
angular.module('jobDetail.hostSummary', [])
|
||||
.controller('HostSummaryController', controller)
|
||||
.run(['$stateExtender', function($stateExtender){
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
@ -1,240 +0,0 @@
|
||||
/** @define SetupItem */
|
||||
|
||||
@import '../shared/branding/colors.less';
|
||||
@import '../shared/branding/colors.default.less';
|
||||
@import '../shared/layouts/one-plus-two.less';
|
||||
|
||||
@breakpoint-md: 1200px;
|
||||
@breakpoint-sm: 623px;
|
||||
|
||||
.JobDetail-tasks.section{
|
||||
margin-top:40px;
|
||||
}
|
||||
.JobDetail-instructions{
|
||||
color: @default-interface-txt;
|
||||
margin: 10px 0 10px 0;
|
||||
|
||||
.badge {
|
||||
background-color: @default-list-header-bg;
|
||||
color: @default-interface-txt;
|
||||
padding: 5px 7px;
|
||||
}
|
||||
}
|
||||
.JobDetail{
|
||||
.OnePlusTwo-container(100%, @breakpoint-md);
|
||||
|
||||
&.fullscreen {
|
||||
.JobDetail-rightSide {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.JobDetail-leftSide{
|
||||
.OnePlusTwo-left--panel(100%, @breakpoint-md);
|
||||
}
|
||||
|
||||
.JobDetail-rightSide{
|
||||
.OnePlusTwo-right--panel(100%, @breakpoint-md);
|
||||
@media (max-width: @breakpoint-md - 1px) {
|
||||
padding-right: 15px;
|
||||
}
|
||||
}
|
||||
.JobDetail-panelHeader{
|
||||
display: flex;
|
||||
height: 30px;
|
||||
}
|
||||
.JobDetail-expandContainer{
|
||||
flex: 1;
|
||||
margin: 0px;
|
||||
line-height: 30px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.JobDetail-panelHeaderText{
|
||||
color: @default-interface-txt;
|
||||
flex: 1 0 auto;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.JobDetail-panelHeaderText:hover{
|
||||
color: @default-interface-txt;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.JobDetail-expandArrow{
|
||||
color: @default-icon-hov;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-transform: uppercase;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.JobDetail-resultsDetails{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
padding-top: 25px;
|
||||
@media screen and(max-width: @breakpoint-sm){
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.JobDetail-resultRow{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
padding-bottom: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.JobDetail-resultRow--variables {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.JobDetail-resultRowLabel{
|
||||
text-transform: uppercase;
|
||||
color: @default-interface-txt;
|
||||
font-size: 14px;
|
||||
font-weight: normal!important;
|
||||
width: 30%;
|
||||
margin-right: 20px;
|
||||
@media screen and(max-width: @breakpoint-md){
|
||||
flex: 2.5 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.JobDetail-resultRowLabel--fullWidth {
|
||||
width: 100%;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.JobDetail-resultRowText{
|
||||
width: ~"calc(70% - 20px)";
|
||||
flex: 1 0 auto;
|
||||
text-transform: none;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.JobDetail-resultRowText--fullWidth {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.JobDetail-searchHeaderRow{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
height: 50px;
|
||||
margin-top: 20px;
|
||||
@media screen and(max-width: @breakpoint-sm){
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.JobDetail-searchContainer{
|
||||
flex: 2 0 auto;
|
||||
@media screen and(max-width: @breakpoint-sm){
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.JobDetail-tableToggleContainer{
|
||||
flex: 1 0 auto;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.JobDetail-tableToggle{
|
||||
padding-left:10px;
|
||||
padding-right: 10px;
|
||||
border: 1px solid @d7grey;
|
||||
}
|
||||
|
||||
.JobDetail-tableToggle.active{
|
||||
background-color: @default-link;
|
||||
border: 1px solid @default-link;
|
||||
color: @default-bg;
|
||||
|
||||
&:hover {
|
||||
background-color: @default-link-hov;
|
||||
}
|
||||
}
|
||||
.JobDetail .nvd3.nv-noData{
|
||||
color: @default-interface-txt;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
}
|
||||
.JobDetail .nv-series{
|
||||
padding-right: 30px;
|
||||
display: block;
|
||||
}
|
||||
.JobDetail-instructions .badge{
|
||||
background-color: @default-list-header-bg;
|
||||
color: @default-interface-txt;
|
||||
}
|
||||
.JobDetail-tableToggle--left{
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.JobDetail-tableToggle--right{
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.JobDetail-searchInput{
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
|
||||
.JobDetail-tableHeader:last-of-type{
|
||||
text-align:justify;
|
||||
}
|
||||
|
||||
.JobDetail-statusIcon{
|
||||
padding-right: 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;
|
||||
}
|
||||
|
||||
.JobDetails-table--noResults {
|
||||
tr > td {
|
||||
border-top: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.JobDetail-statusIcon--results{
|
||||
padding-left: 0px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.JobDetail-graphSection{
|
||||
height: 320px;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.JobDetail-stdoutActionButton--active{
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
flex:none;
|
||||
width:0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.JobDetail-leftSide.JobDetail-stdoutActionButton--active {
|
||||
margin-right: 0px;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,435 +0,0 @@
|
||||
<div class="tab-pane" id="jobs-detail">
|
||||
<div ng-cloak id="htmlTemplate" class="JobDetail" ng-class="{'fullscreen': stdoutFullScreen}">
|
||||
<div ui-view></div>
|
||||
<!--beginning of job-detail-container (left side) -->
|
||||
<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-expandContainer">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="lessStatus" href="" ng-click="toggleLessStatus()">
|
||||
<translate>RESULTS</translate><i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
</a>
|
||||
<a class="JobDetail-panelHeaderText" ng-show="!lessStatus" href="" ng-click="toggleLessStatus()">
|
||||
<translate>RESULTS</translate><i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="JobDetail-actions">
|
||||
<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="icon-launch"></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 class="form-horizontal JobDetail-resultsDetails" role="form" id="job-status-form">
|
||||
<div class="form-group JobDetail-resultRow toggle-show">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Status</label>
|
||||
<div class="JobDetail-resultRowText"><i class="JobDetail-statusIcon--results fa icon-job-{{ job_status.status }}"></i> {{ job_status.status_label }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.explanation">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 col-xs-12" translate>Explanation</label>
|
||||
<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>
|
||||
<div class="JobDetail-resultRowText col-lg-10 col-md-10 col-sm-10 col-xs-9 job_status_explanation"
|
||||
ng-show="previousTaskFailed">Previous Task Failed
|
||||
<a
|
||||
href=""
|
||||
id="explanation_help"
|
||||
aw-pop-over="{{ task_detail }}"
|
||||
aw-pop-over-watch="task_detail"
|
||||
data-placement="bottom"
|
||||
data-container="body" class="help-link" over-title="Failure Detail"
|
||||
title=""
|
||||
tabindex="-1">
|
||||
<i class="fa fa-question-circle">
|
||||
</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.traceback">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-12 col-sm-12 col-xs-12" translate>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>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_template_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Template</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ job_template_url }}" aw-tool-tip="Edit the job template" data-placement="top">{{ job_template_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Started</label>
|
||||
<div class="JobDetail-resultRowText">{{ job_status.started | longDate }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_type">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Job Type</label>
|
||||
<div class="JobDetail-resultRowText">{{ job_type }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Finished</label>
|
||||
<div class="JobDetail-resultRowText">{{ job_status.finished | longDate }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="created_by">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Launched By</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ users_url }}" aw-tool-tip="Edit the User" data-placement="top">{{ created_by }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job_status.started">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Elapsed</label>
|
||||
<div class="JobDetail-resultRowText">{{ job_status.elapsed }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="scheduled_by">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Launched By</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href aw-tool-tip="Edit the Schedule" data-placement="top" ng-click="editSchedule()">{{scheduled_by}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="inventory_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Inventory</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ inventory_url }}" aw-tool-tip="Edit the inventory" data-placement="top">{{ inventory_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="project_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Project</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ project_url }}" aw-tool-tip="Edit the project" data-placement="top">{{ project_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.playbook">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Playbook</label>
|
||||
<div class="JobDetail-resultRowText">{{ job.playbook }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="credential_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Machine Credential</label>
|
||||
<div class="JobDetail-resultRowText JobDetail-resultRowText">
|
||||
<a href="{{ credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ credential_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="cloud_credential_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Cloud Credential</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ cloud_credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ cloud_credential_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="network_credential_name">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Network Credential</label>
|
||||
<div class="JobDetail-resultRowText">
|
||||
<a href="{{ network_credential_url }}" aw-tool-tip="Edit the credential" data-placement="top">{{ network_credential_name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.forks">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Forks</label>
|
||||
<div class="JobDetail-resultRowText">{{ job.forks }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.limit">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Limit</label>
|
||||
<div class="JobDetail-resultRowText">{{ job.limit }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="verbosity">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Verbosity</label>
|
||||
<div class="JobDetail-resultRowText">{{ verbosity }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.job_tags">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Job Tags</label>
|
||||
<div class="JobDetail-resultRowText">{{ job.job_tags }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow toggle-show" ng-show="job.skip_tags">
|
||||
<label class="JobDetail-resultRowLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Skip Tags</label>
|
||||
<div class="JobDetail-resultRowText">{{ job.skip_tags }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group JobDetail-resultRow JobDetail-resultRow--variables toggle-show" ng-show="variables">
|
||||
<label class="JobDetail-resultRowLabel JobDetail-extraVarsLabel col-lg-2 col-md-2 col-sm-2 col-xs-3 control-label" translate>Extra Variables</label>
|
||||
<textarea rows="6" ng-model="variables" name="variables" class="JobDetail-extraVars" id="pre-formatted-variables"></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!--- end of results-->
|
||||
|
||||
<!--beginning of details-->
|
||||
<div id="job-detail-panel" class="JobDetail-resultsContainer Panel" ng-show="!stdoutFullScreen">
|
||||
<div class="JobDetail-panelHeader">
|
||||
<div class="JobDetail-expandContainer">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="lessDetail" href="" ng-click="toggleLessDetail()">
|
||||
<translate>DETAILS</translate><i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
</a>
|
||||
<a class="JobDetail-panelHeaderText" ng-show="!lessDetail" href="" ng-click="toggleLessDetail()">
|
||||
<translate>DETAILS</translate><i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="job-detail-details">
|
||||
<div id="play-section">
|
||||
<div class="JobDetail-instructions"><span class="badge">1</span> Please select from a play below to view its associated tasks.</div>
|
||||
<div class="JobDetail-searchHeaderRow">
|
||||
<div class="JobDetail-searchContainer form-group">
|
||||
<div class="search-name">
|
||||
<input type="text" class="JobDetail-searchInput form-control List-searchInput" id="search_play_name" ng-model="search_play_name" placeholder="Play Name" ng-keypress="searchPlaysKeyPress($event)" >
|
||||
<a class="List-searchInputIcon search-icon" ng-show="searchPlaysEnabled" ng-click="searchPlays()"><i class="fa fa-search"></i></a>
|
||||
<a class="List-searchInputIcon search-icon" ng-show="!searchPlaysEnabled" ng-click="search_play_name=''; searchPlays()"><i class="fa fa-times"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="JobDetail-tableToggleContainer form-group">
|
||||
<div class="btn-group" aw-toggle-button data-after-toggle="filterPlayStatus">
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-primary active" translate>All</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-default" translate>Failed</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="plays-table-header" class="table-header" ng-show="plays.length !== 0">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="List-tableHeader col-lg-7 col-md-6 col-sm-6 col-xs-4" translate>Plays</th>
|
||||
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3" translate>Started</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3" translate>Elapsed</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div id="plays-table-detail" class="table-detail" lr-infinite-scroll="playsScrollDown"
|
||||
scroll-threshold="10" time-threshold="500">
|
||||
<table class="table" ng-class="{'JobDetails-table--noResults': plays.length === 0}">
|
||||
<tbody>
|
||||
<tr class="List-tableRow cursor-pointer" ng-repeat="play in plays" ng-class-odd="'List-tableRow--oddRow'" ng-class-even="'List-tableRow--evenRow'" ng-class="play.playActiveClass" ng-click="selectPlay(play.id, $event)">
|
||||
<td class="List-tableCell col-lg-7 col-md-6 col-sm-6 col-xs-4 status-column" aw-tool-tip="{{ play.status_tip }}" data-tip-watch="play.status_tip" data-placement="top"><i class="JobDetail-statusIcon fa icon-job-{{ play.status }}"></i>{{ play.name }}</td>
|
||||
<td class="List-tableCell col-lg-2 col-md-2 col-sm-2 col-xs-3">{{ play.created | date: 'HH:mm:ss' }}</td>
|
||||
<td class="List-tableCell col-lg-2 col-md-2 col-sm-2 col-xs-3" aw-tool-tip="{{ play.finishedTip }}" data-tip-watch="play.finishedTip"
|
||||
data-placement="top">{{ play.elapsed }}</td>
|
||||
|
||||
</tr>
|
||||
<tr ng-show="plays.length === 0 && waiting">
|
||||
<td colspan="4" class="col-lg-12 loading-info">Waiting...</td>
|
||||
</tr>
|
||||
<tr ng-show="plays.length === 0 && playsLoading && !waiting">
|
||||
<td colspan="4" class="col-lg-12 loading-info">Loading...</td>
|
||||
</tr>
|
||||
<tr ng-show="plays.length === 0 && !playsLoading && !waiting">
|
||||
<td colspan="4" class="col-lg-12 loading-info">No matching plays</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="scroll-spinner" id="playsMoreRows">
|
||||
<i class="fa fa-cog fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end of plays section of details-->
|
||||
|
||||
<div id="task-section" class="section JobDetail-tasks" >
|
||||
<div class="JobDetail-instructions"><span class="badge">2</span> <translate>Please select a task below to view its associated hosts</translate></div>
|
||||
<div class="JobDetail-searchHeaderRow">
|
||||
<div class="JobDetail-searchContainer form-group">
|
||||
<div class="search-name">
|
||||
<input type="text" class="JobDetail-searchInput form-control List-searchInput" id="search_task_name" ng-model="search_task_name" placeholder="Task Name" ng-keypress="searchTasksKeyPress($event)" >
|
||||
<a class="List-searchInputIcon search-icon" ng-show="searchTasksEnabled" ng-click="searchTasks()"><i class="fa fa-search"></i></a>
|
||||
<a class="List-searchInputIcon search-icon" ng-show="!searchTasksEnabled" ng-click="search_task_name=''; searchTasks()"><i class="fa fa-times"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="JobDetail-tableToggleContainer form-group">
|
||||
<div class="btn-group" aw-toggle-button data-after-toggle="filterTaskStatus">
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-primary active" translate>All</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-default" translate>Failed</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-header">
|
||||
<table id="tasks-table-header" class="table table-condensed" ng-show="taskList.length !== 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="List-tableHeader col-lg-3 col-md-3 col-sm-6 col-xs-4" translate>Tasks</th>
|
||||
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3" translate>Started</th>
|
||||
<th class="List-tableHeader col-lg-2 col-md-2 col-sm-2 col-xs-3" translate>Elapsed</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-4 col-md-3 hidden-xs hidden-sm" translate>Host Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div id="tasks-table-detail" class="table-detail" lr-infinite-scroll="tasksScrollDown"
|
||||
scroll-threshold="10" time-threshold="500">
|
||||
<table class="table" ng-class="{'JobDetails-table--noResults': taskList.length === 0}">
|
||||
<tbody>
|
||||
<tr class="List-tableRow cursor-pointer" ng-class-odd="'List-tableRow--oddRow'" ng-class-even="'List-tableRow--evenRow'" ng-repeat="task in taskList = (tasks) track by $index" ng-class="task.taskActiveClass" ng-click="selectTask(task.id)">
|
||||
<td class="List-tableCell col-lg-3 col-md-3 col-sm-6 col-xs-4 status-column" aw-tool-tip="{{ task.status_tip }}"
|
||||
data-tip-watch="task.status_tip" data-placement="top"><i class="JobDetail-statusIcon fa icon-job-{{ task.status }}"></i>{{ task.name }}</td>
|
||||
<td class="List-tableCell col-lg-2 col-md-2 col-sm-2 col-xs-3">{{ task.created | date: 'HH:mm:ss' }}</td>
|
||||
<td class="List-tableCell col-lg-2 col-md-2 col-sm-2 col-xs-3" aw-tool-tip="{{ task.finishedTip }}" data-tip-watch="task.finishedTip"
|
||||
data-placement="top">{{ task.elapsed }}</td>
|
||||
|
||||
<td class="List-tableCell col-lg-4 col-md-3 hidden-sm hidden-xs">
|
||||
<div>
|
||||
|
||||
<a href="" id="{{ task.id }}-successful-bar" aw-tool-tip="{{ task.successfulCountTip }}" data-tip-watch="task.successfulCountTip" data-placement="top" ng-style="task.successfulStyle">
|
||||
<span class="badge successful-hosts">{{ task.successfulCount }}</span>
|
||||
</a>
|
||||
<a href="" id="{{ task.id }}-changed-bar" aw-tool-tip="{{ task.changedCountTip }}" data-tip-watch="task.changedCountTip" data-placement="top" ng-style="task.changedStyle">
|
||||
<span class="badge changed-hosts">{{ task.changedCount }}</span>
|
||||
</a>
|
||||
<a href="" id="{{ task.id }}-skipped-bar" aw-tool-tip="{{ task.skippedCountTip }}" data-tip-watch="task.skippedCountTip" data-placement="top" ng-style="task.skippedStyle">
|
||||
<span class="badge skipped-hosts">{{ task.skippedCount }}</span>
|
||||
</a>
|
||||
<a href="" id="{{ task.id }}-failed-bar" aw-tool-tip="{{ task.failedCountTip }}" data-tip-watch="task.failedCountTip" data-placement="top" ng-style="task.failedStyle">
|
||||
<span class="badge failed-hosts">{{ task.failedCount }}</span>
|
||||
</a>
|
||||
<a href="" id="{{ task.id }}-unreachable-bar" aw-tool-tip="{{ task.unreachableCountTip }}" data-tip-watch="task.unreachableCountTip" data-placement="top" ng-style="task.unreachableStyle">
|
||||
<span class="badge unreachable-hosts">{{ task.unreachableCount }}</span>
|
||||
</a>
|
||||
<a href="" id="{{ task.id }}-missing-bar" aw-tool-tip="{{ task.missingCountTip }}" data-tip-watch="task.missingCountTip" data-placement="top" ng-style="task.missingStyle">
|
||||
<span class="badge missing-hosts">{{ task.missingCount }}</span>
|
||||
</a>
|
||||
<div class="no-matching-hosts inner-bar" id="{{ task.id }}-{{ task.play_id }}-no-matching-hosts-bar" aw-tool-tip="No matching hosts were found." data-placement="top" style="width: 100%;" ng-show="task.status === 'no-matching-hosts'">
|
||||
<translate>No matching hosts.</translate>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="taskList.length === 0 && waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>Waiting...</td>
|
||||
</tr>
|
||||
<tr ng-show="taskList.length === 0 && tasksLoading && !waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>Loading...</td>
|
||||
</tr>
|
||||
<tr ng-show="taskList.length === 0 && !tasksLoading && !waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>No matching tasks</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="scroll-spinner" id="tasksMoreRows"><i class="fa fa-cog fa-spin"></i></div>
|
||||
</div><!-- section -->
|
||||
<!--end of tasks section of details-->
|
||||
|
||||
<div id="task-hosts-section" class="section">
|
||||
<div class="JobDetail-instructions"><span class="badge">3</span> Please select a host below to view associated task details.</div>
|
||||
<div class="JobDetail-searchHeaderRow">
|
||||
<div class="JobDetail-searchContainer form-group">
|
||||
<div class="search-name">
|
||||
<input type="text" class="JobDetail-searchInput form-control List-searchInput" id="search_host_name" ng-model="search_host_name" placeholder="Host Name" ng-keypress="searchHostsKeyPress($event)" >
|
||||
<a class="List-searchInputIcon search-icon" ng-show="searchHostsEnabled" ng-click="searchHosts()"><i class="fa fa-search"></i></a>
|
||||
<a class="List-searchInputIcon search-icon" ng-show="!searchHostsEnabled" ng-click="search_host_name=''; searchHosts()"><i class="fa fa-times"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="JobDetail-tableToggleContainer form-group">
|
||||
<div class="btn-group" aw-toggle-button data-after-toggle="filterHostStatus">
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-primary active" translate>All</button>
|
||||
<button class="JobDetail-tableToggle btn btn-xs btn-default" translate>Failed</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-header" id="hosts-table-header">
|
||||
<table class="table table-condensed" ng-show="results.length !== 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="List-tableHeader col-lg-4 col-md-3 col-sm-3 col-xs-3" translate>Hosts</th>
|
||||
<th class="List-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3" translate>Item</th>
|
||||
<th class="List-tableHeader JobDetail-tableHeader col-lg-3 col-md-4 col-sm-3 col-xs-3" translate>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="hosts-table-detail" class="table-detail" lr-infinite-scroll="hostResultsScrollDown" scroll-threshold="10" time-threshold="500">
|
||||
<table class="table" ng-class="{'JobDetails-table--noResults': results.length === 0}">
|
||||
<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">
|
||||
<td class="List-tableCell col-lg-4 col-md-3 col-sm-3 col-xs-3 status-column">
|
||||
<a ui-sref="jobDetail.host-event.details({eventId: result.id, taskId: selectedTask})" aw-tool-tip="Event ID: {{ result.id }}<br \>Status: {{ result.status_text }}. Click for details" data-tip-watch="result.tip" 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">{{ result.msg }}</td>
|
||||
</tr>
|
||||
<tr ng-show="results.length === 0 && waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>Waiting...</td>
|
||||
</tr>
|
||||
<tr ng-show="results.length === 0 && hostResultsLoading && !waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>Loading...</td>
|
||||
</tr>
|
||||
<tr ng-show="results.length === 0 && !hostResultsLoading && !waiting">
|
||||
<td colspan="5" class="col-lg-12 loading-info" translate>No matching host events</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="scroll-spinner" id="hostResultsMoreRows"><i class="fa fa-cog fa-spin"></i></div>
|
||||
</div>
|
||||
<!--end of hosts section of details-->
|
||||
</div>
|
||||
</div>
|
||||
<!--end of details-->
|
||||
|
||||
<!--beginning of events summary-->
|
||||
<div id="events-summary-panel" class="JobDetail-resultsContainer Panel" ng-show="!stdoutFullScreen">
|
||||
<div class="JobDetail-panelHeader">
|
||||
<div class="JobDetail-expandContainer">
|
||||
<a class="JobDetail-panelHeaderText" ng-show="lessEvents" ui-sref="jobDetail.host-summary" ng-click="toggleLessEvents()">
|
||||
<translate>EVENT SUMMARY</translate><i class="JobDetail-expandArrow fa fa-caret-right"></i>
|
||||
</a>
|
||||
<a class="JobDetail-panelHeaderText" ng-show="!lessEvents" ui-sref="jobDetail" ng-click="toggleLessEvents()">
|
||||
<translate>EVENT SUMMARY</translate><i class="JobDetail-expandArrow fa fa-caret-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Host Summary view -->
|
||||
<div id="events-summary" ng-hide="lessEvents">
|
||||
<div ui-view="host-summary"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end of events summary-->
|
||||
|
||||
|
||||
</div>
|
||||
<!--end of job-detail-container (left side)-->
|
||||
|
||||
<!--beginning of stdout-->
|
||||
<div class="JobDetail-rightSide">
|
||||
<div class="JobDetail-stdoutPanel Panel">
|
||||
<div class="StandardOut-panelHeader">
|
||||
<div class="StandardOut-panelHeaderText" translate>STANDARD OUT</div>
|
||||
<div class="StandardOut-panelHeaderActions">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{'Toggle Output'|translate}}" data-placement="top" ng-class="{'StandardOut-actionButton--active': stdoutFullScreen}" ng-click="toggleStdoutFullscreen()">
|
||||
<i class="fa fa-arrows-alt"></i>
|
||||
</button>
|
||||
<a ng-show="job_status.status === 'failed' || job_status.status === 'successful' || job_status.status === 'canceled'" href="/api/v1/jobs/{{ job.id }}/stdout?format=txt_download&token={{ token }}">
|
||||
<button class="StandardOut-actionButton" aw-tool-tip="{{'Download Output'|translate}}" 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 id="host-modal-dialog" style="display: none;" class="dialog-content"></div>
|
||||
|
||||
</div>
|
||||
@ -1,91 +0,0 @@
|
||||
// <<<<<<< 4cf6a946a1aa14b7d64a8e1e8dabecfd3d056f27
|
||||
// //<<<<<<< bc59236851902d7c768aa26abdb7dc9c9dc27a5a
|
||||
// /*************************************************
|
||||
// * Copyright (c) 2016 Ansible, Inc.
|
||||
// *
|
||||
// * All Rights Reserved
|
||||
// *************************************************/
|
||||
//
|
||||
// // <<<<<<< a3d9eea2c9ddb4e16deec9ec38dea16bf37c559d
|
||||
// // import { templateUrl } from '../shared/template-url/template-url.factory';
|
||||
// //
|
||||
// // export default {
|
||||
// // name: 'jobDetail',
|
||||
// // url: '/jobs/{id: int}',
|
||||
// // ncyBreadcrumb: {
|
||||
// // parent: 'jobs',
|
||||
// // label: "{{ job.id }} - {{ job.name }}"
|
||||
// // },
|
||||
// // data: {
|
||||
// // socket: {
|
||||
// // "groups": {
|
||||
// // "jobs": ["status_changed", "summary"],
|
||||
// // "job_events": []
|
||||
// // }
|
||||
// // }
|
||||
// // },
|
||||
// // templateUrl: templateUrl('job-detail/job-detail'),
|
||||
// // controller: 'JobDetailController'
|
||||
// // };
|
||||
// // =======
|
||||
// // import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
// //
|
||||
// // export default {
|
||||
// // name: 'jobDetail',
|
||||
// // url: '/jobs/:id',
|
||||
// // ncyBreadcrumb: {
|
||||
// // parent: 'jobs',
|
||||
// // label: "{{ job.id }} - {{ job.name }}"
|
||||
// // },
|
||||
// // socket: {
|
||||
// // "groups":{
|
||||
// // "jobs": ["status_changed", "summary"],
|
||||
// // "job_events": []
|
||||
// // }
|
||||
// // },
|
||||
// // templateUrl: templateUrl('job-detail/job-detail'),
|
||||
// // controller: 'JobDetailController'
|
||||
// // };
|
||||
// //=======
|
||||
// =======
|
||||
// >>>>>>> Rebase of devel (w/ channels) + socket rework for new job details
|
||||
// // /*************************************************
|
||||
// // * Copyright (c) 2016 Ansible, Inc.
|
||||
// // *
|
||||
// // * All Rights Reserved
|
||||
// // *************************************************/
|
||||
// //
|
||||
// // import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
// //
|
||||
// // export default {
|
||||
// // name: 'jobDetail',
|
||||
// // url: '/jobs/:id',
|
||||
// // ncyBreadcrumb: {
|
||||
// // parent: 'jobs',
|
||||
// // label: "{{ job.id }} - {{ job.name }}"
|
||||
// // },
|
||||
// // socket: {
|
||||
// // "groups":{
|
||||
// // "jobs": ["status_changed", "summary"],
|
||||
// // "job_events": []
|
||||
// // }
|
||||
// // },
|
||||
// // resolve: {
|
||||
// // jobEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) {
|
||||
// // if (!$rootScope.event_socket) {
|
||||
// // $rootScope.event_socket = Socket({
|
||||
// // scope: $rootScope,
|
||||
// // endpoint: "job_events"
|
||||
// // });
|
||||
// // $rootScope.event_socket.init();
|
||||
// // // returns should really be providing $rootScope.event_socket
|
||||
// // // otherwise, we have to inject the entire $rootScope into the controller
|
||||
// // return true;
|
||||
// // } else {
|
||||
// // return true;
|
||||
// // }
|
||||
// // }]
|
||||
// // },
|
||||
// // templateUrl: templateUrl('job-detail/job-detail'),
|
||||
// // controller: 'JobDetailController'
|
||||
// // };
|
||||
@ -1,215 +0,0 @@
|
||||
export default
|
||||
['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', function($rootScope, Rest, GetBasePath, ProcessErrors){
|
||||
return {
|
||||
stringifyParams: function(params){
|
||||
return _.reduce(params, (result, value, key) => {
|
||||
return result + key + '=' + value + '&';
|
||||
}, '');
|
||||
},
|
||||
|
||||
// the the API passes through Ansible's event_data response
|
||||
// we need to massage away the verbose & redundant stdout/stderr properties
|
||||
processJson: function(data){
|
||||
// configure fields to ignore
|
||||
var ignored = [
|
||||
'type',
|
||||
'event_data',
|
||||
'related',
|
||||
'summary_fields',
|
||||
'url',
|
||||
'ansible_facts',
|
||||
];
|
||||
// remove ignored properties
|
||||
var result = _.chain(data).cloneDeep().forEach(function(value, key, collection){
|
||||
if (ignored.indexOf(key) > -1){
|
||||
delete collection[key];
|
||||
}
|
||||
}).value();
|
||||
return result;
|
||||
},
|
||||
// Return Ansible's passed-through response msg on a job_event
|
||||
processEventMsg: function(event){
|
||||
return typeof event.event_data.res === 'object' ? event.event_data.res.msg : event.event_data.res;
|
||||
},
|
||||
// Return only Ansible's passed-through response item on a job_event
|
||||
processEventItem: function(event){
|
||||
try{
|
||||
var item = event.event_data.res.item;
|
||||
return typeof item === 'object' ? JSON.stringify(item) : item;
|
||||
}
|
||||
catch(err){return;}
|
||||
},
|
||||
processsEventTip: function(event, status){
|
||||
try{
|
||||
var string = `Event ID: ${ event.id }<br>Status: ${ _.capitalize(status.status)}. Click for details`;
|
||||
return typeof item === 'object' ? JSON.stringify(string) : string;
|
||||
}
|
||||
catch(err){return;}
|
||||
},
|
||||
// Generate a helper class for job_event statuses
|
||||
// the stack for which status to display is
|
||||
// unreachable > failed > changed > ok
|
||||
// uses the API's runner events and convenience properties .failed .changed to determine status.
|
||||
// see: job_event_callback.py for more filters to support
|
||||
processEventStatus: function(event){
|
||||
if (event.event === 'runner_on_unreachable'){
|
||||
return {
|
||||
class: 'HostEvents-status--unreachable',
|
||||
status: 'unreachable'
|
||||
};
|
||||
}
|
||||
// equiv to 'runner_on_error' && 'runner on failed'
|
||||
if (event.failed){
|
||||
return {
|
||||
class: 'HostEvents-status--failed',
|
||||
status: 'failed'
|
||||
};
|
||||
}
|
||||
// catch the changed case before ok, because both can be true
|
||||
if (event.changed){
|
||||
return {
|
||||
class: 'HostEvents-status--changed',
|
||||
status: 'changed'
|
||||
};
|
||||
}
|
||||
if (event.event === 'runner_on_ok' || event.event === 'runner_on_async_ok'){
|
||||
return {
|
||||
class: 'HostEvents-status--ok',
|
||||
status: 'ok'
|
||||
};
|
||||
}
|
||||
if (event.event === 'runner_on_skipped'){
|
||||
return {
|
||||
class: 'HostEvents-status--skipped',
|
||||
status: 'skipped'
|
||||
};
|
||||
}
|
||||
},
|
||||
// Consumes a response from this.getRelatedJobEvents(id, params)
|
||||
// returns an array for view logic to iterate over to build host result rows
|
||||
processHostEvents: function(data){
|
||||
var self = this;
|
||||
var results = [];
|
||||
data.forEach(function(event){
|
||||
if (event.event !== 'runner_on_no_hosts'){
|
||||
var status = self.processEventStatus(event);
|
||||
var msg = self.processEventMsg(event);
|
||||
var item = self.processEventItem(event);
|
||||
var tip = self.processsEventTip(event, status);
|
||||
results.push({
|
||||
id: event.id,
|
||||
status: status.status,
|
||||
status_text: _.capitalize(status.status),
|
||||
host_id: event.host,
|
||||
task_id: event.parent,
|
||||
name: event.event_data.host,
|
||||
created: event.created,
|
||||
tip: typeof tip === 'undefined' ? undefined : tip,
|
||||
msg: typeof msg === 'undefined' ? undefined : msg,
|
||||
item: typeof item === 'undefined' ? undefined : item
|
||||
});
|
||||
}
|
||||
});
|
||||
return results;
|
||||
},
|
||||
// GET events related to a job run
|
||||
// e.g.
|
||||
// ?event=playbook_on_stats
|
||||
// ?parent=206&event__startswith=runner&page_size=200&order=host_name,counter
|
||||
getRelatedJobEvents: function(id, params){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id + '/job_events/?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
return data;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
getJobEventChildren: function(uuid){
|
||||
var url = GetBasePath('job_events');
|
||||
url = `${url}?parent__uuid=${uuid}&order_by=host_name`;
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
return data;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
// GET job host summaries related to a job run
|
||||
// e.g. ?page_size=200&order=host_name
|
||||
getJobHostSummaries: function(id, params){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id + '/job_host_summaries/?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
return data;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
// GET job plays related to a job run
|
||||
// e.g. ?page_size=200
|
||||
getJobPlays: function(id, params){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id + '/job_plays/?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
return data;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
getJobTasks: function(id, params){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id + '/job_tasks/?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
return data;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
getJob: function(params){
|
||||
var url = GetBasePath('unified_jobs') + '?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
return data;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
// GET next set of paginated results
|
||||
// expects 'next' param returned by the API e.g.
|
||||
// "/api/v1/jobs/51/job_plays/?order_by=id&page=2&page_size=1"
|
||||
getNextPage: function(url){
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
return data;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
@ -1,24 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
// import route from './job-detail.route';
|
||||
import controller from './job-detail.controller';
|
||||
import service from './job-detail.service';
|
||||
import hostEvents from './host-events/main';
|
||||
// import hostEvent from './host-event/main';
|
||||
import hostSummary from './host-summary/main';
|
||||
|
||||
export default
|
||||
angular.module('jobDetail', [
|
||||
hostEvents.name,
|
||||
// hostEvent.name,
|
||||
hostSummary.name
|
||||
])
|
||||
.controller('JobDetailController', controller)
|
||||
.service('JobDetailService', service);
|
||||
// .run(['$stateExtender', function($stateExtender) {
|
||||
// $stateExtender.addState(route);
|
||||
// }]);
|
||||
@ -9,7 +9,7 @@
|
||||
</a>
|
||||
<span class="HostEvent-title">{{event.host_name}}</span>
|
||||
<!-- close -->
|
||||
<button ui-sref="jobDetail" type="button" class="close">
|
||||
<button ui-sref="jobResult" type="button" class="close">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -40,19 +40,19 @@
|
||||
|
||||
<div class="HostEvent-nav">
|
||||
<!-- view navigation buttons -->
|
||||
<button ui-sref="jobDetail.host-event.json" type="button"
|
||||
<button ui-sref="jobResult.host-event.json" type="button"
|
||||
class="btn btn-sm btn-default HostEvent-tab"
|
||||
ng-class="{'HostEvent-tab--selected' : isActiveState('jobDetail.host-event.json')}">
|
||||
ng-class="{'HostEvent-tab--selected' : isActiveState('jobResult.host-event.json')}">
|
||||
JSON
|
||||
</button>
|
||||
<button ng-if="stdout" ui-sref="jobDetail.host-event.stdout"
|
||||
<button ng-if="stdout" ui-sref="jobResult.host-event.stdout"
|
||||
type="button" class="btn btn-sm btn-default HostEvent-tab"
|
||||
ng-class="{'HostEvent-tab--selected' : isActiveState('jobDetail.host-event.stdout')}">
|
||||
ng-class="{'HostEvent-tab--selected' : isActiveState('jobResult.host-event.stdout')}">
|
||||
Standard Out
|
||||
</button>
|
||||
<button ng-if="stderr" ui-sref="jobDetail.host-event.stderr"
|
||||
<button ng-if="stderr" ui-sref="jobResult.host-event.stderr"
|
||||
type="button" class="btn btn-sm btn-default HostEvent-tab"
|
||||
ng-class="{'HostEvent-tab--selected' : isActiveState('jobDetail.host-event.stderr')}">
|
||||
ng-class="{'HostEvent-tab--selected' : isActiveState('jobResult.host-event.stderr')}">
|
||||
Standard Error
|
||||
</button>
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
|
||||
<!-- controls -->
|
||||
<div class="HostEvent-controls">
|
||||
<button ui-sref="jobDetail" class="btn btn-sm btn-default HostEvent-close">Close</button>
|
||||
<button ui-sref="jobResult" class="btn btn-sm btn-default HostEvent-close">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -6,23 +6,15 @@
|
||||
|
||||
|
||||
export default
|
||||
['$stateParams', '$scope', '$state', 'Wait', 'JobDetailService', 'hostEvent', 'hostResults',
|
||||
function($stateParams, $scope, $state, Wait, JobDetailService, hostEvent, hostResults){
|
||||
['$stateParams', '$scope', '$state', 'Wait', 'jobResultsService', 'hostEvent',
|
||||
function($stateParams, $scope, $state, Wait, jobResultsService, hostEvent){
|
||||
|
||||
$scope.processEventStatus = JobDetailService.processEventStatus;
|
||||
$scope.hostResults = [];
|
||||
// Avoid rendering objects in the details fieldset
|
||||
// ng-if="processResults(value)" via host-event-details.partial.html
|
||||
$scope.processEventStatus = jobResultsService.processEventStatus;
|
||||
$scope.processResults = function(value){
|
||||
if (typeof value === 'object'){return false;}
|
||||
else {return true;}
|
||||
};
|
||||
$scope.isStdOut = function(){
|
||||
if ($state.current.name === 'jobDetail.host-event.stdout' || $state.current.name === 'jobDetail.host-event.stderr'){
|
||||
return 'StandardOut-preContainer StandardOut-preContent';
|
||||
}
|
||||
};
|
||||
/*ignore jslint start*/
|
||||
|
||||
var initCodeMirror = function(el, data, mode){
|
||||
var container = document.getElementById(el);
|
||||
var editor = CodeMirror.fromTextArea(container, { // jshint ignore:line
|
||||
@ -37,17 +29,9 @@
|
||||
return $state.current.name === name;
|
||||
};
|
||||
|
||||
$scope.getActiveHostIndex = function(){
|
||||
var result = $scope.hostResults.filter(function( obj ) {
|
||||
return obj.id === $scope.event.id;
|
||||
});
|
||||
return $scope.hostResults.indexOf(result[0]);
|
||||
};
|
||||
|
||||
var init = function(){
|
||||
hostEvent.event_name = hostEvent.event;
|
||||
$scope.event = _.cloneDeep(hostEvent);
|
||||
$scope.hostResults = hostResults;
|
||||
|
||||
// grab standard out & standard error if present from the host
|
||||
// event's "res" object, for things like Ansible modules
|
||||
@ -72,7 +56,7 @@
|
||||
}
|
||||
// instantiate Codemirror
|
||||
// try/catch pattern prevents the abstract-state controller from complaining about element being null
|
||||
if ($state.current.name === 'jobDetail.host-event.json'){
|
||||
if ($state.current.name === 'jobResult.host-event.json'){
|
||||
try{
|
||||
initCodeMirror('HostEvent-codemirror', JSON.stringify($scope.json, null, 4), {name: "javascript", json: true});
|
||||
}
|
||||
@ -80,7 +64,7 @@
|
||||
// element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController
|
||||
}
|
||||
}
|
||||
else if ($state.current.name === 'jobDetail.host-event.stdout'){
|
||||
else if ($state.current.name === 'jobResult.host-event.stdout'){
|
||||
try{
|
||||
initCodeMirror('HostEvent-codemirror', $scope.stdout, 'shell');
|
||||
}
|
||||
@ -88,7 +72,7 @@
|
||||
// element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController
|
||||
}
|
||||
}
|
||||
else if ($state.current.name === 'jobDetail.host-event.stderr'){
|
||||
else if ($state.current.name === 'jobResult.host-event.stderr'){
|
||||
try{
|
||||
initCodeMirror('HostEvent-codemirror', $scope.stderr, 'shell');
|
||||
}
|
||||
|
||||
@ -7,20 +7,17 @@
|
||||
import { templateUrl } from '../../shared/template-url/template-url.factory';
|
||||
|
||||
var hostEventModal = {
|
||||
name: 'jobDetail.host-event',
|
||||
name: 'jobResult.host-event',
|
||||
url: '/host-event/:eventId',
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-results/host-event/host-event-modal'),
|
||||
'abstract': false,
|
||||
resolve: {
|
||||
hostEvent: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) {
|
||||
return JobDetailService.getRelatedJobEvents($stateParams.id, {
|
||||
hostEvent: ['jobResultsService', '$stateParams', function(jobResultsService, $stateParams) {
|
||||
return jobResultsService.getRelatedJobEvents($stateParams.id, {
|
||||
id: $stateParams.eventId
|
||||
}).then(function(res) {
|
||||
return res.data.results[0]; });
|
||||
}],
|
||||
hostResults: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) {
|
||||
return JobDetailService.getJobEventChildren($stateParams.taskId).then(res => res.data.results);
|
||||
}]
|
||||
},
|
||||
onExit: function() {
|
||||
@ -34,21 +31,21 @@ var hostEventModal = {
|
||||
};
|
||||
|
||||
var hostEventJson = {
|
||||
name: 'jobDetail.host-event.json',
|
||||
name: 'jobResult.host-event.json',
|
||||
url: '/json',
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-results/host-event/host-event-codemirror')
|
||||
};
|
||||
|
||||
var hostEventStdout = {
|
||||
name: 'jobDetail.host-event.stdout',
|
||||
name: 'jobResult.host-event.stdout',
|
||||
url: '/stdout',
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-results/host-event/host-event-stdout')
|
||||
};
|
||||
|
||||
var hostEventStderr = {
|
||||
name: 'jobDetail.host-event.stderr',
|
||||
name: 'jobResult.host-event.stderr',
|
||||
url: '/stderr',
|
||||
controller: 'HostEventController',
|
||||
templateUrl: templateUrl('job-results/host-event/host-event-stderr')
|
||||
|
||||
@ -108,7 +108,7 @@
|
||||
ng-show="!previousTaskFailed">
|
||||
{{job.job_explanation}}
|
||||
</div>
|
||||
<div class="JobDetail-resultRowText "
|
||||
<div class="jobResult-resultRowText "
|
||||
ng-show="previousTaskFailed">Previous Task Failed
|
||||
<a
|
||||
href=""
|
||||
|
||||
@ -13,7 +13,7 @@ const defaultParams = {
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'jobDetail',
|
||||
name: 'jobResult',
|
||||
url: '/jobs/{id: int}',
|
||||
searchPrefix: 'job_event',
|
||||
ncyBreadcrumb: {
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
*************************************************/
|
||||
|
||||
|
||||
export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', 'GetBasePath', 'Alert',
|
||||
function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun, GetBasePath, Alert) {
|
||||
export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', 'GetBasePath', 'Alert', '$rootScope',
|
||||
function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun, GetBasePath, Alert, $rootScope) {
|
||||
var val = {
|
||||
// the playbook_on_stats event returns the count data in a weird format.
|
||||
// format to what we need!
|
||||
@ -190,6 +190,87 @@ function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybo
|
||||
});
|
||||
|
||||
return val.promise;
|
||||
},
|
||||
// Generate a helper class for job_event statuses
|
||||
// the stack for which status to display is
|
||||
// unreachable > failed > changed > ok
|
||||
// uses the API's runner events and convenience properties .failed .changed to determine status.
|
||||
// see: job_event_callback.py for more filters to support
|
||||
processEventStatus: function(event){
|
||||
if (event.event === 'runner_on_unreachable'){
|
||||
return {
|
||||
class: 'HostEvent-status--unreachable',
|
||||
status: 'unreachable'
|
||||
};
|
||||
}
|
||||
// equiv to 'runner_on_error' && 'runner on failed'
|
||||
if (event.failed){
|
||||
return {
|
||||
class: 'HostEvent-status--failed',
|
||||
status: 'failed'
|
||||
};
|
||||
}
|
||||
// catch the changed case before ok, because both can be true
|
||||
if (event.changed){
|
||||
return {
|
||||
class: 'HostEvent-status--changed',
|
||||
status: 'changed'
|
||||
};
|
||||
}
|
||||
if (event.event === 'runner_on_ok' || event.event === 'runner_on_async_ok'){
|
||||
return {
|
||||
class: 'HostEvent-status--ok',
|
||||
status: 'ok'
|
||||
};
|
||||
}
|
||||
if (event.event === 'runner_on_skipped'){
|
||||
return {
|
||||
class: 'HostEvent-status--skipped',
|
||||
status: 'skipped'
|
||||
};
|
||||
}
|
||||
},
|
||||
// GET events related to a job run
|
||||
// e.g.
|
||||
// ?event=playbook_on_stats
|
||||
// ?parent=206&event__startswith=runner&page_size=200&order=host_name,counter
|
||||
getRelatedJobEvents: function(id, params){
|
||||
var url = GetBasePath('jobs');
|
||||
url = url + id + '/job_events/?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(data){
|
||||
return data;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
stringifyParams: function(params){
|
||||
return _.reduce(params, (result, value, key) => {
|
||||
return result + key + '=' + value + '&';
|
||||
}, '');
|
||||
},
|
||||
// the the API passes through Ansible's event_data response
|
||||
// we need to massage away the verbose & redundant stdout/stderr properties
|
||||
processJson: function(data){
|
||||
// configure fields to ignore
|
||||
var ignored = [
|
||||
'type',
|
||||
'event_data',
|
||||
'related',
|
||||
'summary_fields',
|
||||
'url',
|
||||
'ansible_facts',
|
||||
];
|
||||
// remove ignored properties
|
||||
var result = _.chain(data).cloneDeep().forEach(function(value, key, collection){
|
||||
if (ignored.indexOf(key) > -1){
|
||||
delete collection[key];
|
||||
}
|
||||
}).value();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
return val;
|
||||
|
||||
@ -77,7 +77,7 @@ export default ['$log', 'moment', function($log, moment){
|
||||
return `"`;
|
||||
}
|
||||
else{
|
||||
return ` JobResultsStdOut-stdoutColumn--clickable" ui-sref="jobDetail.host-event.json({eventId: ${event.id}, taskUuid: '${event.event_data.task_uuid}' })" aw-tool-tip="Event ID: ${event.id} <br>Status: ${event.event_display} <br>Click for details" data-placement="top"`;
|
||||
return ` JobResultsStdOut-stdoutColumn--clickable" ui-sref="jobResult.host-event.json({eventId: ${event.id}, taskUuid: '${event.event_data.task_uuid}' })" aw-tool-tip="Event ID: ${event.id} <br>Status: ${event.event_display} <br>Click for details" data-placement="top"`;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@ -124,12 +124,12 @@ export default
|
||||
if($rootScope.portalMode===false && Empty(data.system_job) || (base === 'home')){
|
||||
// use $state.go with reload: true option to re-instantiate sockets in
|
||||
|
||||
var goToJobDetails = function(state) {
|
||||
var goTojobResults = function(state) {
|
||||
$state.go(state, {id: job}, {reload:true});
|
||||
};
|
||||
|
||||
if(_.has(data, 'job')) {
|
||||
goToJobDetails('jobDetail');
|
||||
goTojobResults('jobResult');
|
||||
} else if(base === 'jobs'){
|
||||
if(scope.clearDialog) {
|
||||
scope.clearDialog();
|
||||
@ -137,20 +137,20 @@ export default
|
||||
return;
|
||||
} else if(data.type && data.type === 'workflow_job') {
|
||||
job = data.id;
|
||||
goToJobDetails('workflowResults');
|
||||
goTojobResults('workflowResults');
|
||||
}
|
||||
else if(_.has(data, 'ad_hoc_command')) {
|
||||
goToJobDetails('adHocJobStdout');
|
||||
goTojobResults('adHocJobStdout');
|
||||
}
|
||||
else if(_.has(data, 'system_job')) {
|
||||
goToJobDetails('managementJobStdout');
|
||||
goTojobResults('managementJobStdout');
|
||||
}
|
||||
else if(_.has(data, 'project_update')) {
|
||||
// If we are on the projects list or any child state of that list
|
||||
// then we want to stay on that page. Otherwise go to the stdout
|
||||
// view.
|
||||
if(!$state.includes('projects')) {
|
||||
goToJobDetails('scmUpdateStdout');
|
||||
goTojobResults('scmUpdateStdout');
|
||||
}
|
||||
}
|
||||
else if(_.has(data, 'inventory_update')) {
|
||||
@ -158,7 +158,7 @@ export default
|
||||
// page then we want to stay on that page. Otherwise go to the stdout
|
||||
// view.
|
||||
if(!$state.includes('inventoryManage')) {
|
||||
goToJobDetails('inventorySyncStdout');
|
||||
goTojobResults('inventorySyncStdout');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ export default
|
||||
|
||||
// As of 3.0, the only place the user can relaunch a
|
||||
// playbook is on jobTemplates.edit (completed_jobs tab),
|
||||
// jobs, and jobDetails $states.
|
||||
// jobs, and jobResults $states.
|
||||
|
||||
if (!$scope.submitJobRelaunch) {
|
||||
if($scope.submitJobType && $scope.submitJobType === 'job_template') {
|
||||
@ -236,18 +236,18 @@ export default
|
||||
// Go out and get some of the job details like inv, cred, name
|
||||
Rest.setUrl(GetBasePath('jobs') + $scope.submitJobId);
|
||||
Rest.get()
|
||||
.success(function (jobDetailData) {
|
||||
.success(function (jobResultData) {
|
||||
$scope.job_template_data = {
|
||||
name: jobDetailData.name
|
||||
name: jobResultData.name
|
||||
};
|
||||
$scope.defaults = {};
|
||||
if(jobDetailData.summary_fields.inventory) {
|
||||
$scope.defaults.inventory = angular.copy(jobDetailData.summary_fields.inventory);
|
||||
$scope.selected_inventory = angular.copy(jobDetailData.summary_fields.inventory);
|
||||
if(jobResultData.summary_fields.inventory) {
|
||||
$scope.defaults.inventory = angular.copy(jobResultData.summary_fields.inventory);
|
||||
$scope.selected_inventory = angular.copy(jobResultData.summary_fields.inventory);
|
||||
}
|
||||
if(jobDetailData.summary_fields.credential) {
|
||||
$scope.defaults.credential = angular.copy(jobDetailData.summary_fields.credential);
|
||||
$scope.selected_credential = angular.copy(jobDetailData.summary_fields.credential);
|
||||
if(jobResultData.summary_fields.credential) {
|
||||
$scope.defaults.credential = angular.copy(jobResultData.summary_fields.credential);
|
||||
$scope.selected_credential = angular.copy(jobResultData.summary_fields.credential);
|
||||
updateRequiredPasswords();
|
||||
}
|
||||
initiateModal();
|
||||
|
||||
@ -112,29 +112,29 @@
|
||||
RelaunchJob({ scope: $scope, id: typeId, type: job.type, name: job.name });
|
||||
};
|
||||
|
||||
$scope.viewJobDetails = function(job) {
|
||||
$scope.viewjobResults = function(job) {
|
||||
|
||||
var goToJobDetails = function(state) {
|
||||
var goTojobResults = function(state) {
|
||||
$state.go(state, { id: job.id }, { reload: true });
|
||||
};
|
||||
switch (job.type) {
|
||||
case 'job':
|
||||
goToJobDetails('jobDetail');
|
||||
goTojobResults('jobResult');
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
goToJobDetails('adHocJobStdout');
|
||||
goTojobResults('adHocJobStdout');
|
||||
break;
|
||||
case 'system_job':
|
||||
goToJobDetails('managementJobStdout');
|
||||
goTojobResults('managementJobStdout');
|
||||
break;
|
||||
case 'project_update':
|
||||
goToJobDetails('scmUpdateStdout');
|
||||
goTojobResults('scmUpdateStdout');
|
||||
break;
|
||||
case 'inventory_update':
|
||||
goToJobDetails('inventorySyncStdout');
|
||||
goTojobResults('inventorySyncStdout');
|
||||
break;
|
||||
case 'workflow_job':
|
||||
goToJobDetails('workflowResults');
|
||||
goTojobResults('workflowResults');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -30,12 +30,12 @@ export default
|
||||
dataTitle: "{{ job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ job.status }}',
|
||||
iconOnly: true,
|
||||
ngClick:"viewJobDetails(job)",
|
||||
ngClick:"viewjobResults(job)",
|
||||
nosort: true
|
||||
},
|
||||
id: {
|
||||
label: 'ID',
|
||||
ngClick:"viewJobDetails(job)",
|
||||
ngClick:"viewjobResults(job)",
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||
awToolTip: "{{ job.status_tip }}",
|
||||
dataPlacement: 'top',
|
||||
@ -44,7 +44,7 @@ export default
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
|
||||
ngClick: "viewJobDetails(job)",
|
||||
ngClick: "viewjobResults(job)",
|
||||
badgePlacement: 'right',
|
||||
badgeCustom: true,
|
||||
badgeIcon: `<a href="{{ job.workflow_result_link }}"
|
||||
@ -90,7 +90,7 @@ export default
|
||||
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
|
||||
"view": {
|
||||
mode: "all",
|
||||
ngClick: "viewJobDetails(job)",
|
||||
ngClick: "viewjobResults(job)",
|
||||
awToolTip: i18n._("View the job"),
|
||||
dataPlacement: "top"
|
||||
},
|
||||
|
||||
@ -30,11 +30,11 @@ export default
|
||||
dataTitle: "{{ completed_job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ completed_job.status }}',
|
||||
iconOnly: true,
|
||||
ngClick:"viewJobDetails(completed_job)",
|
||||
ngClick:"viewjobResults(completed_job)",
|
||||
},
|
||||
id: {
|
||||
label: 'ID',
|
||||
ngClick:"viewJobDetails(completed_job)",
|
||||
ngClick:"viewjobResults(completed_job)",
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||
awToolTip: "{{ completed_job.status_tip }}",
|
||||
dataPlacement: 'top'
|
||||
@ -42,7 +42,7 @@ export default
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
|
||||
ngClick: "viewJobDetails(completed_job)",
|
||||
ngClick: "viewjobResults(completed_job)",
|
||||
awToolTip: "{{ completed_job.name | sanitize }}",
|
||||
dataPlacement: 'top'
|
||||
},
|
||||
|
||||
@ -20,7 +20,7 @@ export default
|
||||
fields: {
|
||||
id: {
|
||||
label: 'ID',
|
||||
ngClick:"viewJobDetails(job)",
|
||||
ngClick:"viewjobResults(job)",
|
||||
key: true,
|
||||
desc: true,
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2',
|
||||
@ -35,7 +35,7 @@ export default
|
||||
dataTitle: "{{ job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ job.status }}',
|
||||
iconOnly: true,
|
||||
ngClick:"viewJobDetails(job)"
|
||||
ngClick:"viewjobResults(job)"
|
||||
},
|
||||
started: {
|
||||
label: 'Started',
|
||||
@ -52,7 +52,7 @@ export default
|
||||
name: {
|
||||
label: 'Name',
|
||||
columnClass: 'col-md-3 col-xs-5',
|
||||
ngClick: "viewJobDetails(job)",
|
||||
ngClick: "viewjobResults(job)",
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -177,10 +177,10 @@ export default ['$scope', '$rootScope', '$location', '$log',
|
||||
$state.go('projects.edit', { project_id: id });
|
||||
};
|
||||
|
||||
if ($scope.removeGoToJobDetails) {
|
||||
$scope.removeGoToJobDetails();
|
||||
if ($scope.removeGoTojobResults) {
|
||||
$scope.removeGoTojobResults();
|
||||
}
|
||||
$scope.removeGoToJobDetails = $scope.$on('GoToJobDetails', function(e, data) {
|
||||
$scope.removeGoTojobResults = $scope.$on('GoTojobResults', function(e, data) {
|
||||
if (data.summary_fields.current_update || data.summary_fields.last_update) {
|
||||
|
||||
Wait('start');
|
||||
@ -207,7 +207,7 @@ export default ['$scope', '$rootScope', '$location', '$log',
|
||||
Rest.setUrl(project.url);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.$emit('GoToJobDetails', data);
|
||||
$scope.$emit('GoTojobResults', data);
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
|
||||
@ -132,10 +132,10 @@ export default ['$scope', '$rootScope', '$location', '$log', '$stateParams',
|
||||
$state.go('projects.edit', { project_id: id });
|
||||
};
|
||||
|
||||
if ($scope.removeGoToJobDetails) {
|
||||
$scope.removeGoToJobDetails();
|
||||
if ($scope.removeGoTojobResults) {
|
||||
$scope.removeGoTojobResults();
|
||||
}
|
||||
$scope.removeGoToJobDetails = $scope.$on('GoToJobDetails', function(e, data) {
|
||||
$scope.removeGoTojobResults = $scope.$on('GoTojobResults', function(e, data) {
|
||||
if (data.summary_fields.current_update || data.summary_fields.last_update) {
|
||||
|
||||
Wait('start');
|
||||
@ -162,7 +162,7 @@ export default ['$scope', '$rootScope', '$location', '$log', '$stateParams',
|
||||
Rest.setUrl(project.url);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.$emit('GoToJobDetails', data);
|
||||
$scope.$emit('GoTojobResults', data);
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
RESULTS
|
||||
</div>
|
||||
<div class="StandardOut-actions">
|
||||
<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="icon-launch"></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 === 'waiting' || job.status === 'running' || job.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 === 'waiting' || job.status === 'running' || job.status === 'pending' " aw-tool-tip="Delete" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
<button id="relaunch-job-button" class="List-actionButton jobResult-launchButton" data-placement="top" mode="all" ng-click="relaunchJob()" aw-tool-tip="Relaunch using the same parameters" data-original-title="" title=""><i class="icon-launch"></i> </button>
|
||||
<button id="cancel-job-button" class="List-actionButton List-actionButton--delete jobResult-launchButton" data-placement="top" ng-click="deleteJob()" ng-show="job.status === 'waiting' || job.status === 'running' || job.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 jobResult-launchButton" data-placement="top" ng-click="deleteJob()" ng-hide="job.status === 'waiting' || job.status === 'running' || job.status === 'pending' " aw-tool-tip="Delete" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="StandardOut-details">
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
RESULTS
|
||||
</div>
|
||||
<div class="StandardOut-actions">
|
||||
<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="icon-launch"></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 === 'waiting' || job.status === 'running' || job.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 === 'waiting' || job.status === 'running' || job.status === 'pending' " aw-tool-tip="Delete" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
<button id="relaunch-job-button" class="List-actionButton jobResult-launchButton" data-placement="top" mode="all" ng-click="relaunchJob()" aw-tool-tip="Relaunch using the same parameters" data-original-title="" title=""><i class="icon-launch"></i> </button>
|
||||
<button id="cancel-job-button" class="List-actionButton List-actionButton--delete jobResult-launchButton" data-placement="top" ng-click="deleteJob()" ng-show="job.status === 'waiting' || job.status === 'running' || job.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 jobResult-launchButton" data-placement="top" ng-click="deleteJob()" ng-hide="job.status === 'waiting' || job.status === 'running' || job.status === 'pending' " aw-tool-tip="Delete" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="StandardOut-details">
|
||||
|
||||
@ -20,7 +20,7 @@ export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'Proce
|
||||
|
||||
// Open up a socket for events depending on the type of job
|
||||
function openSockets() {
|
||||
if ($state.current.name === 'jobDetail') {
|
||||
if ($state.current.name === 'jobResult') {
|
||||
$log.debug("socket watching on job_events-" + job_id);
|
||||
$scope.$on(`ws-job_events-${job_id}`, function() {
|
||||
$log.debug("socket fired on job_events-" + job_id);
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
RESULTS
|
||||
</div>
|
||||
<div class="StandardOut-actions">
|
||||
<button id="cancel-job-button" class="List-actionButton List-actionButton--delete JobDetail-launchButton" data-placement="top" ng-click="deleteJob()" ng-show="job.status === 'waiting' || job.status === 'running' || job.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 === 'waiting' || job.status === 'running' || job.status === 'pending' " aw-tool-tip="Delete" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
<button id="cancel-job-button" class="List-actionButton List-actionButton--delete jobResult-launchButton" data-placement="top" ng-click="deleteJob()" ng-show="job.status === 'waiting' || job.status === 'running' || job.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 jobResult-launchButton" data-placement="top" ng-click="deleteJob()" ng-hide="job.status === 'waiting' || job.status === 'running' || job.status === 'pending' " aw-tool-tip="Delete" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="StandardOut-details">
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
RESULTS
|
||||
</div>
|
||||
<div class="StandardOut-actions">
|
||||
<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'|translate}}" data-original-title="" title=""><i class="icon-launch"></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 === 'waiting' || job.status === 'running' || job.status === 'pending'" aw-tool-tip="{{'Cancel'|translate}}" 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 === 'waiting' || job.status === 'running' || job.status === 'pending' " aw-tool-tip="{{'Delete'|translate}}" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
<button id="relaunch-job-button" class="List-actionButton jobResult-launchButton" data-placement="top" mode="all" ng-click="relaunchJob()" aw-tool-tip="{{'Relaunch using the same parameters'|translate}}" data-original-title="" title=""><i class="icon-launch"></i> </button>
|
||||
<button id="cancel-job-button" class="List-actionButton List-actionButton--delete jobResult-launchButton" data-placement="top" ng-click="deleteJob()" ng-show="job.status === 'waiting' || job.status === 'running' || job.status === 'pending'" aw-tool-tip="{{'Cancel'|translate}}" data-original-title="" title=""><i class="fa fa-minus-circle"></i> </button>
|
||||
<button id="delete-job-button" class="List-actionButton List-actionButton--delete jobResult-launchButton" data-placement="top" ng-click="deleteJob()" ng-hide="job.status === 'waiting' || job.status === 'running' || job.status === 'pending' " aw-tool-tip="{{'Delete'|translate}}" data-original-title="" title=""><i class="fa fa-trash-o"></i> </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="StandardOut-details">
|
||||
|
||||
@ -33,14 +33,14 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams,
|
||||
|
||||
if (data.status === 'failed' || data.status === 'canceled' || data.status === 'error' || data.status === 'successful') {
|
||||
// Go out and refresh the job details
|
||||
getJobDetails();
|
||||
getjobResults();
|
||||
}
|
||||
});
|
||||
|
||||
// Set the parse type so that CodeMirror knows how to display extra params YAML/JSON
|
||||
$scope.parseType = 'yaml';
|
||||
|
||||
function getJobDetails() {
|
||||
function getjobResults() {
|
||||
|
||||
// Go out and get the job details based on the job type. jobType gets defined
|
||||
// in the data block of the route declaration for each of the different types
|
||||
@ -260,7 +260,7 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams,
|
||||
RelaunchJob({ scope: $scope, id: typeId, type: job.type, name: job.name });
|
||||
};
|
||||
|
||||
getJobDetails();
|
||||
getjobResults();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -830,7 +830,7 @@ export default [ '$state','moment', '$timeout', '$window',
|
||||
this.on("click", function(d) {
|
||||
if(d.job.id && d.unifiedJobTemplate) {
|
||||
if(d.unifiedJobTemplate.unified_job_type === 'job') {
|
||||
$state.go('jobDetail', {id: d.job.id});
|
||||
$state.go('jobResult', {id: d.job.id});
|
||||
}
|
||||
else if(d.unifiedJobTemplate.unified_job_type === 'inventory_update') {
|
||||
$state.go('inventorySyncStdout', {id: d.job.id});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user