mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 17:37:37 -02:30
Merge pull request #5594 from jaredevantabor/decouple-jobDetails-jobResults
RIP Old Job Details Page
This commit is contained in:
@@ -1984,10 +1984,6 @@ tr td button i {
|
|||||||
width: 73px;
|
width: 73px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.JobDetails-status {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.red-text {
|
.red-text {
|
||||||
color: @red;
|
color: @red;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import inventories from './inventories/main';
|
|||||||
import inventoryScripts from './inventory-scripts/main';
|
import inventoryScripts from './inventory-scripts/main';
|
||||||
import organizations from './organizations/main';
|
import organizations from './organizations/main';
|
||||||
import managementJobs from './management-jobs/main';
|
import managementJobs from './management-jobs/main';
|
||||||
import jobDetail from './job-detail/main';
|
|
||||||
import workflowResults from './workflow-results/main';
|
import workflowResults from './workflow-results/main';
|
||||||
import jobResults from './job-results/main';
|
import jobResults from './job-results/main';
|
||||||
import jobSubmission from './job-submission/main';
|
import jobSubmission from './job-submission/main';
|
||||||
@@ -119,7 +118,6 @@ var tower = angular.module('Tower', [
|
|||||||
login.name,
|
login.name,
|
||||||
activityStream.name,
|
activityStream.name,
|
||||||
footer.name,
|
footer.name,
|
||||||
jobDetail.name,
|
|
||||||
workflowResults.name,
|
workflowResults.name,
|
||||||
jobResults.name,
|
jobResults.name,
|
||||||
jobSubmission.name,
|
jobSubmission.name,
|
||||||
@@ -191,7 +189,6 @@ var tower = angular.module('Tower', [
|
|||||||
'LogViewerStatusDefinition',
|
'LogViewerStatusDefinition',
|
||||||
'StandardOutHelper',
|
'StandardOutHelper',
|
||||||
'LogViewerOptionsDefinition',
|
'LogViewerOptionsDefinition',
|
||||||
'JobDetailHelper',
|
|
||||||
'lrInfiniteScroll',
|
'lrInfiniteScroll',
|
||||||
'LoadConfigHelper',
|
'LoadConfigHelper',
|
||||||
'PortalJobsListDefinition',
|
'PortalJobsListDefinition',
|
||||||
@@ -350,19 +347,19 @@ var tower = angular.module('Tower', [
|
|||||||
|
|
||||||
$rootScope.$on("$stateChangeStart", function (event, next) {
|
$rootScope.$on("$stateChangeStart", function (event, next) {
|
||||||
// Remove any lingering intervals
|
// Remove any lingering intervals
|
||||||
// except on jobDetails.* states
|
// except on jobResults.* states
|
||||||
var jobDetailStates = [
|
var jobResultStates = [
|
||||||
'jobDetail',
|
'jobResult',
|
||||||
'jobDetail.host-summary',
|
'jobResult.host-summary',
|
||||||
'jobDetail.host-event.details',
|
'jobResult.host-event.details',
|
||||||
'jobDetail.host-event.json',
|
'jobResult.host-event.json',
|
||||||
'jobDetail.host-events',
|
'jobResult.host-events',
|
||||||
'jobDetail.host-event.stdout'
|
'jobResult.host-event.stdout'
|
||||||
];
|
];
|
||||||
if ($rootScope.jobDetailInterval && !_.includes(jobDetailStates, next.name) ) {
|
if ($rootScope.jobResultInterval && !_.includes(jobResultStates, next.name) ) {
|
||||||
window.clearInterval($rootScope.jobDetailInterval);
|
window.clearInterval($rootScope.jobResultInterval);
|
||||||
}
|
}
|
||||||
if ($rootScope.jobStdOutInterval && !_.includes(jobDetailStates, next.name) ) {
|
if ($rootScope.jobStdOutInterval && !_.includes(jobResultStates, next.name) ) {
|
||||||
window.clearInterval($rootScope.jobStdOutInterval);
|
window.clearInterval($rootScope.jobStdOutInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import Credentials from "./helpers/Credentials";
|
|||||||
import Events from "./helpers/Events";
|
import Events from "./helpers/Events";
|
||||||
import Groups from "./helpers/Groups";
|
import Groups from "./helpers/Groups";
|
||||||
import Hosts from "./helpers/Hosts";
|
import Hosts from "./helpers/Hosts";
|
||||||
import JobDetail from "./helpers/JobDetail";
|
|
||||||
import JobSubmission from "./helpers/JobSubmission";
|
import JobSubmission from "./helpers/JobSubmission";
|
||||||
import JobTemplates from "./helpers/JobTemplates";
|
import JobTemplates from "./helpers/JobTemplates";
|
||||||
import Jobs from "./helpers/Jobs";
|
import Jobs from "./helpers/Jobs";
|
||||||
@@ -38,7 +37,6 @@ export
|
|||||||
Events,
|
Events,
|
||||||
Groups,
|
Groups,
|
||||||
Hosts,
|
Hosts,
|
||||||
JobDetail,
|
|
||||||
JobSubmission,
|
JobSubmission,
|
||||||
JobTemplates,
|
JobTemplates,
|
||||||
Jobs,
|
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>
|
</a>
|
||||||
<span class="HostEvent-title">{{event.host_name}}</span>
|
<span class="HostEvent-title">{{event.host_name}}</span>
|
||||||
<!-- close -->
|
<!-- close -->
|
||||||
<button ui-sref="jobDetail" type="button" class="close">
|
<button ui-sref="jobResult" type="button" class="close">
|
||||||
<i class="fa fa-times-circle"></i>
|
<i class="fa fa-times-circle"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,19 +40,19 @@
|
|||||||
|
|
||||||
<div class="HostEvent-nav">
|
<div class="HostEvent-nav">
|
||||||
<!-- view navigation buttons -->
|
<!-- 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"
|
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
|
JSON
|
||||||
</button>
|
</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"
|
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
|
Standard Out
|
||||||
</button>
|
</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"
|
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
|
Standard Error
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
<!-- controls -->
|
<!-- controls -->
|
||||||
<div class="HostEvent-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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,23 +6,15 @@
|
|||||||
|
|
||||||
|
|
||||||
export default
|
export default
|
||||||
['$stateParams', '$scope', '$state', 'Wait', 'JobDetailService', 'hostEvent', 'hostResults',
|
['$stateParams', '$scope', '$state', 'Wait', 'jobResultsService', 'hostEvent',
|
||||||
function($stateParams, $scope, $state, Wait, JobDetailService, hostEvent, hostResults){
|
function($stateParams, $scope, $state, Wait, jobResultsService, hostEvent){
|
||||||
|
|
||||||
$scope.processEventStatus = JobDetailService.processEventStatus;
|
$scope.processEventStatus = jobResultsService.processEventStatus;
|
||||||
$scope.hostResults = [];
|
|
||||||
// Avoid rendering objects in the details fieldset
|
|
||||||
// ng-if="processResults(value)" via host-event-details.partial.html
|
|
||||||
$scope.processResults = function(value){
|
$scope.processResults = function(value){
|
||||||
if (typeof value === 'object'){return false;}
|
if (typeof value === 'object'){return false;}
|
||||||
else {return true;}
|
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 initCodeMirror = function(el, data, mode){
|
||||||
var container = document.getElementById(el);
|
var container = document.getElementById(el);
|
||||||
var editor = CodeMirror.fromTextArea(container, { // jshint ignore:line
|
var editor = CodeMirror.fromTextArea(container, { // jshint ignore:line
|
||||||
@@ -37,17 +29,9 @@
|
|||||||
return $state.current.name === 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]);
|
|
||||||
};
|
|
||||||
|
|
||||||
var init = function(){
|
var init = function(){
|
||||||
hostEvent.event_name = hostEvent.event;
|
hostEvent.event_name = hostEvent.event;
|
||||||
$scope.event = _.cloneDeep(hostEvent);
|
$scope.event = _.cloneDeep(hostEvent);
|
||||||
$scope.hostResults = hostResults;
|
|
||||||
|
|
||||||
// grab standard out & standard error if present from the host
|
// grab standard out & standard error if present from the host
|
||||||
// event's "res" object, for things like Ansible modules
|
// event's "res" object, for things like Ansible modules
|
||||||
@@ -72,7 +56,7 @@
|
|||||||
}
|
}
|
||||||
// instantiate Codemirror
|
// instantiate Codemirror
|
||||||
// try/catch pattern prevents the abstract-state controller from complaining about element being null
|
// 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{
|
try{
|
||||||
initCodeMirror('HostEvent-codemirror', JSON.stringify($scope.json, null, 4), {name: "javascript", json: true});
|
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
|
// 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{
|
try{
|
||||||
initCodeMirror('HostEvent-codemirror', $scope.stdout, 'shell');
|
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
|
// 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{
|
try{
|
||||||
initCodeMirror('HostEvent-codemirror', $scope.stderr, 'shell');
|
initCodeMirror('HostEvent-codemirror', $scope.stderr, 'shell');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,20 +7,17 @@
|
|||||||
import { templateUrl } from '../../shared/template-url/template-url.factory';
|
import { templateUrl } from '../../shared/template-url/template-url.factory';
|
||||||
|
|
||||||
var hostEventModal = {
|
var hostEventModal = {
|
||||||
name: 'jobDetail.host-event',
|
name: 'jobResult.host-event',
|
||||||
url: '/host-event/:eventId',
|
url: '/host-event/:eventId',
|
||||||
controller: 'HostEventController',
|
controller: 'HostEventController',
|
||||||
templateUrl: templateUrl('job-results/host-event/host-event-modal'),
|
templateUrl: templateUrl('job-results/host-event/host-event-modal'),
|
||||||
'abstract': false,
|
'abstract': false,
|
||||||
resolve: {
|
resolve: {
|
||||||
hostEvent: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) {
|
hostEvent: ['jobResultsService', '$stateParams', function(jobResultsService, $stateParams) {
|
||||||
return JobDetailService.getRelatedJobEvents($stateParams.id, {
|
return jobResultsService.getRelatedJobEvents($stateParams.id, {
|
||||||
id: $stateParams.eventId
|
id: $stateParams.eventId
|
||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
return res.data.results[0]; });
|
return res.data.results[0]; });
|
||||||
}],
|
|
||||||
hostResults: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) {
|
|
||||||
return JobDetailService.getJobEventChildren($stateParams.taskId).then(res => res.data.results);
|
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
onExit: function() {
|
onExit: function() {
|
||||||
@@ -34,21 +31,21 @@ var hostEventModal = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var hostEventJson = {
|
var hostEventJson = {
|
||||||
name: 'jobDetail.host-event.json',
|
name: 'jobResult.host-event.json',
|
||||||
url: '/json',
|
url: '/json',
|
||||||
controller: 'HostEventController',
|
controller: 'HostEventController',
|
||||||
templateUrl: templateUrl('job-results/host-event/host-event-codemirror')
|
templateUrl: templateUrl('job-results/host-event/host-event-codemirror')
|
||||||
};
|
};
|
||||||
|
|
||||||
var hostEventStdout = {
|
var hostEventStdout = {
|
||||||
name: 'jobDetail.host-event.stdout',
|
name: 'jobResult.host-event.stdout',
|
||||||
url: '/stdout',
|
url: '/stdout',
|
||||||
controller: 'HostEventController',
|
controller: 'HostEventController',
|
||||||
templateUrl: templateUrl('job-results/host-event/host-event-stdout')
|
templateUrl: templateUrl('job-results/host-event/host-event-stdout')
|
||||||
};
|
};
|
||||||
|
|
||||||
var hostEventStderr = {
|
var hostEventStderr = {
|
||||||
name: 'jobDetail.host-event.stderr',
|
name: 'jobResult.host-event.stderr',
|
||||||
url: '/stderr',
|
url: '/stderr',
|
||||||
controller: 'HostEventController',
|
controller: 'HostEventController',
|
||||||
templateUrl: templateUrl('job-results/host-event/host-event-stderr')
|
templateUrl: templateUrl('job-results/host-event/host-event-stderr')
|
||||||
|
|||||||
@@ -108,7 +108,7 @@
|
|||||||
ng-show="!previousTaskFailed">
|
ng-show="!previousTaskFailed">
|
||||||
{{job.job_explanation}}
|
{{job.job_explanation}}
|
||||||
</div>
|
</div>
|
||||||
<div class="JobDetail-resultRowText "
|
<div class="jobResult-resultRowText "
|
||||||
ng-show="previousTaskFailed">Previous Task Failed
|
ng-show="previousTaskFailed">Previous Task Failed
|
||||||
<a
|
<a
|
||||||
href=""
|
href=""
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const defaultParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'jobDetail',
|
name: 'jobResult',
|
||||||
url: '/jobs/{id: int}',
|
url: '/jobs/{id: int}',
|
||||||
searchPrefix: 'job_event',
|
searchPrefix: 'job_event',
|
||||||
ncyBreadcrumb: {
|
ncyBreadcrumb: {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
export default ['$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) {
|
function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun, GetBasePath, Alert, $rootScope) {
|
||||||
var val = {
|
var val = {
|
||||||
// the playbook_on_stats event returns the count data in a weird format.
|
// the playbook_on_stats event returns the count data in a weird format.
|
||||||
// format to what we need!
|
// format to what we need!
|
||||||
@@ -190,6 +190,87 @@ function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybo
|
|||||||
});
|
});
|
||||||
|
|
||||||
return val.promise;
|
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;
|
return val;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default ['$log', 'moment', function($log, moment){
|
|||||||
return `"`;
|
return `"`;
|
||||||
}
|
}
|
||||||
else{
|
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')){
|
if($rootScope.portalMode===false && Empty(data.system_job) || (base === 'home')){
|
||||||
// use $state.go with reload: true option to re-instantiate sockets in
|
// 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});
|
$state.go(state, {id: job}, {reload:true});
|
||||||
};
|
};
|
||||||
|
|
||||||
if(_.has(data, 'job')) {
|
if(_.has(data, 'job')) {
|
||||||
goToJobDetails('jobDetail');
|
goTojobResults('jobResult');
|
||||||
} else if(base === 'jobs'){
|
} else if(base === 'jobs'){
|
||||||
if(scope.clearDialog) {
|
if(scope.clearDialog) {
|
||||||
scope.clearDialog();
|
scope.clearDialog();
|
||||||
@@ -137,20 +137,20 @@ export default
|
|||||||
return;
|
return;
|
||||||
} else if(data.type && data.type === 'workflow_job') {
|
} else if(data.type && data.type === 'workflow_job') {
|
||||||
job = data.id;
|
job = data.id;
|
||||||
goToJobDetails('workflowResults');
|
goTojobResults('workflowResults');
|
||||||
}
|
}
|
||||||
else if(_.has(data, 'ad_hoc_command')) {
|
else if(_.has(data, 'ad_hoc_command')) {
|
||||||
goToJobDetails('adHocJobStdout');
|
goTojobResults('adHocJobStdout');
|
||||||
}
|
}
|
||||||
else if(_.has(data, 'system_job')) {
|
else if(_.has(data, 'system_job')) {
|
||||||
goToJobDetails('managementJobStdout');
|
goTojobResults('managementJobStdout');
|
||||||
}
|
}
|
||||||
else if(_.has(data, 'project_update')) {
|
else if(_.has(data, 'project_update')) {
|
||||||
// If we are on the projects list or any child state of that list
|
// 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
|
// then we want to stay on that page. Otherwise go to the stdout
|
||||||
// view.
|
// view.
|
||||||
if(!$state.includes('projects')) {
|
if(!$state.includes('projects')) {
|
||||||
goToJobDetails('scmUpdateStdout');
|
goTojobResults('scmUpdateStdout');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(_.has(data, 'inventory_update')) {
|
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
|
// page then we want to stay on that page. Otherwise go to the stdout
|
||||||
// view.
|
// view.
|
||||||
if(!$state.includes('inventoryManage')) {
|
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
|
// As of 3.0, the only place the user can relaunch a
|
||||||
// playbook is on jobTemplates.edit (completed_jobs tab),
|
// playbook is on jobTemplates.edit (completed_jobs tab),
|
||||||
// jobs, and jobDetails $states.
|
// jobs, and jobResults $states.
|
||||||
|
|
||||||
if (!$scope.submitJobRelaunch) {
|
if (!$scope.submitJobRelaunch) {
|
||||||
if($scope.submitJobType && $scope.submitJobType === 'job_template') {
|
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
|
// Go out and get some of the job details like inv, cred, name
|
||||||
Rest.setUrl(GetBasePath('jobs') + $scope.submitJobId);
|
Rest.setUrl(GetBasePath('jobs') + $scope.submitJobId);
|
||||||
Rest.get()
|
Rest.get()
|
||||||
.success(function (jobDetailData) {
|
.success(function (jobResultData) {
|
||||||
$scope.job_template_data = {
|
$scope.job_template_data = {
|
||||||
name: jobDetailData.name
|
name: jobResultData.name
|
||||||
};
|
};
|
||||||
$scope.defaults = {};
|
$scope.defaults = {};
|
||||||
if(jobDetailData.summary_fields.inventory) {
|
if(jobResultData.summary_fields.inventory) {
|
||||||
$scope.defaults.inventory = angular.copy(jobDetailData.summary_fields.inventory);
|
$scope.defaults.inventory = angular.copy(jobResultData.summary_fields.inventory);
|
||||||
$scope.selected_inventory = angular.copy(jobDetailData.summary_fields.inventory);
|
$scope.selected_inventory = angular.copy(jobResultData.summary_fields.inventory);
|
||||||
}
|
}
|
||||||
if(jobDetailData.summary_fields.credential) {
|
if(jobResultData.summary_fields.credential) {
|
||||||
$scope.defaults.credential = angular.copy(jobDetailData.summary_fields.credential);
|
$scope.defaults.credential = angular.copy(jobResultData.summary_fields.credential);
|
||||||
$scope.selected_credential = angular.copy(jobDetailData.summary_fields.credential);
|
$scope.selected_credential = angular.copy(jobResultData.summary_fields.credential);
|
||||||
updateRequiredPasswords();
|
updateRequiredPasswords();
|
||||||
}
|
}
|
||||||
initiateModal();
|
initiateModal();
|
||||||
|
|||||||
@@ -112,29 +112,29 @@
|
|||||||
RelaunchJob({ scope: $scope, id: typeId, type: job.type, name: job.name });
|
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 });
|
$state.go(state, { id: job.id }, { reload: true });
|
||||||
};
|
};
|
||||||
switch (job.type) {
|
switch (job.type) {
|
||||||
case 'job':
|
case 'job':
|
||||||
goToJobDetails('jobDetail');
|
goTojobResults('jobResult');
|
||||||
break;
|
break;
|
||||||
case 'ad_hoc_command':
|
case 'ad_hoc_command':
|
||||||
goToJobDetails('adHocJobStdout');
|
goTojobResults('adHocJobStdout');
|
||||||
break;
|
break;
|
||||||
case 'system_job':
|
case 'system_job':
|
||||||
goToJobDetails('managementJobStdout');
|
goTojobResults('managementJobStdout');
|
||||||
break;
|
break;
|
||||||
case 'project_update':
|
case 'project_update':
|
||||||
goToJobDetails('scmUpdateStdout');
|
goTojobResults('scmUpdateStdout');
|
||||||
break;
|
break;
|
||||||
case 'inventory_update':
|
case 'inventory_update':
|
||||||
goToJobDetails('inventorySyncStdout');
|
goTojobResults('inventorySyncStdout');
|
||||||
break;
|
break;
|
||||||
case 'workflow_job':
|
case 'workflow_job':
|
||||||
goToJobDetails('workflowResults');
|
goTojobResults('workflowResults');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ export default
|
|||||||
dataTitle: "{{ job.status_popover_title }}",
|
dataTitle: "{{ job.status_popover_title }}",
|
||||||
icon: 'icon-job-{{ job.status }}',
|
icon: 'icon-job-{{ job.status }}',
|
||||||
iconOnly: true,
|
iconOnly: true,
|
||||||
ngClick:"viewJobDetails(job)",
|
ngClick:"viewjobResults(job)",
|
||||||
nosort: true
|
nosort: true
|
||||||
},
|
},
|
||||||
id: {
|
id: {
|
||||||
label: 'ID',
|
label: 'ID',
|
||||||
ngClick:"viewJobDetails(job)",
|
ngClick:"viewjobResults(job)",
|
||||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||||
awToolTip: "{{ job.status_tip }}",
|
awToolTip: "{{ job.status_tip }}",
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
@@ -44,7 +44,7 @@ export default
|
|||||||
name: {
|
name: {
|
||||||
label: i18n._('Name'),
|
label: i18n._('Name'),
|
||||||
columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
|
columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
|
||||||
ngClick: "viewJobDetails(job)",
|
ngClick: "viewjobResults(job)",
|
||||||
badgePlacement: 'right',
|
badgePlacement: 'right',
|
||||||
badgeCustom: true,
|
badgeCustom: true,
|
||||||
badgeIcon: `<a href="{{ job.workflow_result_link }}"
|
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',
|
columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
|
||||||
"view": {
|
"view": {
|
||||||
mode: "all",
|
mode: "all",
|
||||||
ngClick: "viewJobDetails(job)",
|
ngClick: "viewjobResults(job)",
|
||||||
awToolTip: i18n._("View the job"),
|
awToolTip: i18n._("View the job"),
|
||||||
dataPlacement: "top"
|
dataPlacement: "top"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ export default
|
|||||||
dataTitle: "{{ completed_job.status_popover_title }}",
|
dataTitle: "{{ completed_job.status_popover_title }}",
|
||||||
icon: 'icon-job-{{ completed_job.status }}',
|
icon: 'icon-job-{{ completed_job.status }}',
|
||||||
iconOnly: true,
|
iconOnly: true,
|
||||||
ngClick:"viewJobDetails(completed_job)",
|
ngClick:"viewjobResults(completed_job)",
|
||||||
},
|
},
|
||||||
id: {
|
id: {
|
||||||
label: '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',
|
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||||
awToolTip: "{{ completed_job.status_tip }}",
|
awToolTip: "{{ completed_job.status_tip }}",
|
||||||
dataPlacement: 'top'
|
dataPlacement: 'top'
|
||||||
@@ -42,7 +42,7 @@ export default
|
|||||||
name: {
|
name: {
|
||||||
label: i18n._('Name'),
|
label: i18n._('Name'),
|
||||||
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
|
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 }}",
|
awToolTip: "{{ completed_job.name | sanitize }}",
|
||||||
dataPlacement: 'top'
|
dataPlacement: 'top'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default
|
|||||||
fields: {
|
fields: {
|
||||||
id: {
|
id: {
|
||||||
label: 'ID',
|
label: 'ID',
|
||||||
ngClick:"viewJobDetails(job)",
|
ngClick:"viewjobResults(job)",
|
||||||
key: true,
|
key: true,
|
||||||
desc: true,
|
desc: true,
|
||||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2',
|
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2',
|
||||||
@@ -35,7 +35,7 @@ export default
|
|||||||
dataTitle: "{{ job.status_popover_title }}",
|
dataTitle: "{{ job.status_popover_title }}",
|
||||||
icon: 'icon-job-{{ job.status }}',
|
icon: 'icon-job-{{ job.status }}',
|
||||||
iconOnly: true,
|
iconOnly: true,
|
||||||
ngClick:"viewJobDetails(job)"
|
ngClick:"viewjobResults(job)"
|
||||||
},
|
},
|
||||||
started: {
|
started: {
|
||||||
label: 'Started',
|
label: 'Started',
|
||||||
@@ -52,7 +52,7 @@ export default
|
|||||||
name: {
|
name: {
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
columnClass: 'col-md-3 col-xs-5',
|
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 });
|
$state.go('projects.edit', { project_id: id });
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($scope.removeGoToJobDetails) {
|
if ($scope.removeGoTojobResults) {
|
||||||
$scope.removeGoToJobDetails();
|
$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) {
|
if (data.summary_fields.current_update || data.summary_fields.last_update) {
|
||||||
|
|
||||||
Wait('start');
|
Wait('start');
|
||||||
@@ -207,7 +207,7 @@ export default ['$scope', '$rootScope', '$location', '$log',
|
|||||||
Rest.setUrl(project.url);
|
Rest.setUrl(project.url);
|
||||||
Rest.get()
|
Rest.get()
|
||||||
.success(function(data) {
|
.success(function(data) {
|
||||||
$scope.$emit('GoToJobDetails', data);
|
$scope.$emit('GoTojobResults', data);
|
||||||
})
|
})
|
||||||
.error(function(data, status) {
|
.error(function(data, status) {
|
||||||
ProcessErrors($scope, data, status, null, {
|
ProcessErrors($scope, data, status, null, {
|
||||||
|
|||||||
@@ -132,10 +132,10 @@ export default ['$scope', '$rootScope', '$location', '$log', '$stateParams',
|
|||||||
$state.go('projects.edit', { project_id: id });
|
$state.go('projects.edit', { project_id: id });
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($scope.removeGoToJobDetails) {
|
if ($scope.removeGoTojobResults) {
|
||||||
$scope.removeGoToJobDetails();
|
$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) {
|
if (data.summary_fields.current_update || data.summary_fields.last_update) {
|
||||||
|
|
||||||
Wait('start');
|
Wait('start');
|
||||||
@@ -162,7 +162,7 @@ export default ['$scope', '$rootScope', '$location', '$log', '$stateParams',
|
|||||||
Rest.setUrl(project.url);
|
Rest.setUrl(project.url);
|
||||||
Rest.get()
|
Rest.get()
|
||||||
.success(function(data) {
|
.success(function(data) {
|
||||||
$scope.$emit('GoToJobDetails', data);
|
$scope.$emit('GoTojobResults', data);
|
||||||
})
|
})
|
||||||
.error(function(data, status) {
|
.error(function(data, status) {
|
||||||
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
|
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
RESULTS
|
RESULTS
|
||||||
</div>
|
</div>
|
||||||
<div class="StandardOut-actions">
|
<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="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 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="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 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="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>
|
</div>
|
||||||
<div class="StandardOut-details">
|
<div class="StandardOut-details">
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
RESULTS
|
RESULTS
|
||||||
</div>
|
</div>
|
||||||
<div class="StandardOut-actions">
|
<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="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 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="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 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="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>
|
</div>
|
||||||
<div class="StandardOut-details">
|
<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
|
// Open up a socket for events depending on the type of job
|
||||||
function openSockets() {
|
function openSockets() {
|
||||||
if ($state.current.name === 'jobDetail') {
|
if ($state.current.name === 'jobResult') {
|
||||||
$log.debug("socket watching on job_events-" + job_id);
|
$log.debug("socket watching on job_events-" + job_id);
|
||||||
$scope.$on(`ws-job_events-${job_id}`, function() {
|
$scope.$on(`ws-job_events-${job_id}`, function() {
|
||||||
$log.debug("socket fired on job_events-" + job_id);
|
$log.debug("socket fired on job_events-" + job_id);
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
RESULTS
|
RESULTS
|
||||||
</div>
|
</div>
|
||||||
<div class="StandardOut-actions">
|
<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="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 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="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>
|
</div>
|
||||||
<div class="StandardOut-details">
|
<div class="StandardOut-details">
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
RESULTS
|
RESULTS
|
||||||
</div>
|
</div>
|
||||||
<div class="StandardOut-actions">
|
<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="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 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="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 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="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>
|
</div>
|
||||||
<div class="StandardOut-details">
|
<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') {
|
if (data.status === 'failed' || data.status === 'canceled' || data.status === 'error' || data.status === 'successful') {
|
||||||
// Go out and refresh the job details
|
// Go out and refresh the job details
|
||||||
getJobDetails();
|
getjobResults();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the parse type so that CodeMirror knows how to display extra params YAML/JSON
|
// Set the parse type so that CodeMirror knows how to display extra params YAML/JSON
|
||||||
$scope.parseType = 'yaml';
|
$scope.parseType = 'yaml';
|
||||||
|
|
||||||
function getJobDetails() {
|
function getjobResults() {
|
||||||
|
|
||||||
// Go out and get the job details based on the job type. jobType gets defined
|
// 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
|
// 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 });
|
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) {
|
this.on("click", function(d) {
|
||||||
if(d.job.id && d.unifiedJobTemplate) {
|
if(d.job.id && d.unifiedJobTemplate) {
|
||||||
if(d.unifiedJobTemplate.unified_job_type === 'job') {
|
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') {
|
else if(d.unifiedJobTemplate.unified_job_type === 'inventory_update') {
|
||||||
$state.go('inventorySyncStdout', {id: d.job.id});
|
$state.go('inventorySyncStdout', {id: d.job.id});
|
||||||
|
|||||||
Reference in New Issue
Block a user