mirror of
https://github.com/ansible/awx.git
synced 2026-05-12 11:57:37 -02:30
adding more workflow results pages and functionality
This commit is contained in:
@@ -48,6 +48,7 @@ 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 jobDetail from './job-detail/main';
|
||||||
|
import workflowResults from './workflow-results/main';
|
||||||
import jobSubmission from './job-submission/main';
|
import jobSubmission from './job-submission/main';
|
||||||
import notifications from './notifications/main';
|
import notifications from './notifications/main';
|
||||||
import about from './about/main';
|
import about from './about/main';
|
||||||
@@ -115,6 +116,7 @@ var tower = angular.module('Tower', [
|
|||||||
activityStream.name,
|
activityStream.name,
|
||||||
footer.name,
|
footer.name,
|
||||||
jobDetail.name,
|
jobDetail.name,
|
||||||
|
workflowResults.name,
|
||||||
jobSubmission.name,
|
jobSubmission.name,
|
||||||
notifications.name,
|
notifications.name,
|
||||||
standardOut.name,
|
standardOut.name,
|
||||||
|
|||||||
@@ -5,10 +5,13 @@
|
|||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
import route from './job-results.route.js';
|
import route from './workflow-results.route.js';
|
||||||
|
|
||||||
|
import workflowResultsService from './workflow-results.service';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('workflowResults', [])
|
angular.module('workflowResults', [])
|
||||||
.run(['$stateExtender', function($stateExtender) {
|
.run(['$stateExtender', function($stateExtender) {
|
||||||
$stateExtender.addState(route);
|
$stateExtender.addState(route);
|
||||||
}]);
|
}])
|
||||||
|
.service('workflowResultsService', workflowResultsService);
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
@import '../shared/branding/colors.less';
|
||||||
|
@import '../shared/branding/colors.default.less';
|
||||||
|
@import '../shared/layouts/one-plus-two.less';
|
||||||
|
|
||||||
|
@breakpoint-md: 1200px;
|
||||||
|
@breakpoint-sm: 623px;
|
||||||
|
|
||||||
|
.WorkflowResults {
|
||||||
|
.OnePlusTwo-container(100%, @breakpoint-md);
|
||||||
|
|
||||||
|
&.fullscreen {
|
||||||
|
.WorkflowResults-rightSide {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-leftSide {
|
||||||
|
.OnePlusTwo-left--panel(100%, @breakpoint-md);
|
||||||
|
// TODO: needs to be set based on height of browser window
|
||||||
|
height: 870px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-rightSide {
|
||||||
|
.OnePlusTwo-right--panel(100%, @breakpoint-md);
|
||||||
|
// TODO: needs to be set based on height of browser window
|
||||||
|
height: 870px !important;
|
||||||
|
|
||||||
|
@media (max-width: @breakpoint-md - 1px) {
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-stdoutActionButton--active {
|
||||||
|
display: none;
|
||||||
|
visibility: hidden;
|
||||||
|
flex:none;
|
||||||
|
width:0px;
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-panelHeader {
|
||||||
|
display: flex;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-panelHeaderText {
|
||||||
|
color: @default-interface-txt;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-resultRow {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-resultRow--variables {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-resultRowLabel--fullWidth {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-resultRowText {
|
||||||
|
width: ~"calc(70% - 20px)";
|
||||||
|
flex: 1 0 auto;
|
||||||
|
text-transform: none;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-resultRowText--fullWidth {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-statusResultIcon {
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-badgeRow {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowResults-badgeTitle{
|
||||||
|
color: @default-interface-txt;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 10px;
|
||||||
|
font-weight: normal;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
export default ['workflowData',
|
||||||
|
'workflowResultsService',
|
||||||
|
'workflowDataOptions',
|
||||||
|
'jobLabels',
|
||||||
|
'workflowNodes',
|
||||||
|
'$scope',
|
||||||
|
'ParseTypeChange',
|
||||||
|
'ParseVariableString',
|
||||||
|
function(workflowData,
|
||||||
|
workflowResultsService,
|
||||||
|
workflowDataOptions,
|
||||||
|
jobLabels,
|
||||||
|
workflowNodes,
|
||||||
|
$scope,
|
||||||
|
ParseTypeChange,
|
||||||
|
ParseVariableString,
|
||||||
|
) {
|
||||||
|
var getTowerLinks = function() {
|
||||||
|
var getTowerLink = function(key) {
|
||||||
|
if ($scope.workflow.related[key]) {
|
||||||
|
return '/#/' + $scope.workflow.related[key]
|
||||||
|
.split('api/v1/')[1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.workflow_template_link = '/#/templates/workflow_job_template/'+$scope.workflow.workflow_job_template;
|
||||||
|
$scope.created_by_link = getTowerLink('created_by');
|
||||||
|
$scope.cloud_credential_link = getTowerLink('cloud_credential');
|
||||||
|
$scope.network_credential_link = getTowerLink('network_credential');
|
||||||
|
};
|
||||||
|
|
||||||
|
var getTowerLabels = function() {
|
||||||
|
var getTowerLabel = function(key) {
|
||||||
|
if ($scope.workflowOptions && $scope.workflowOptions[key]) {
|
||||||
|
return $scope.workflowOptions[key].choices
|
||||||
|
.filter(val => val[0] === $scope.workflow[key])
|
||||||
|
.map(val => val[1])[0];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.status_label = getTowerLabel('status');
|
||||||
|
$scope.type_label = getTowerLabel('job_type');
|
||||||
|
$scope.verbosity_label = getTowerLabel('verbosity');
|
||||||
|
};
|
||||||
|
|
||||||
|
var getTotalHostCount = function(count) {
|
||||||
|
return Object
|
||||||
|
.keys(count).reduce((acc, i) => acc += count[i], 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// put initially resolved request data on scope
|
||||||
|
$scope.workflow = workflowData;
|
||||||
|
$scope.workflow_nodes = workflowNodes;
|
||||||
|
$scope.workflowOptions = workflowDataOptions.actions.GET;
|
||||||
|
$scope.labels = jobLabels;
|
||||||
|
|
||||||
|
// turn related api browser routes into tower routes
|
||||||
|
getTowerLinks();
|
||||||
|
|
||||||
|
// use options labels to manipulate display of details
|
||||||
|
getTowerLabels();
|
||||||
|
|
||||||
|
// set up a read only code mirror for extra vars
|
||||||
|
$scope.variables = ParseVariableString($scope.workflow.extra_vars);
|
||||||
|
$scope.parseType = 'yaml';
|
||||||
|
ParseTypeChange({ scope: $scope,
|
||||||
|
field_id: 'pre-formatted-variables',
|
||||||
|
readOnly: true });
|
||||||
|
|
||||||
|
// Click binding for the expand/collapse button on the standard out log
|
||||||
|
$scope.stdoutFullScreen = false;
|
||||||
|
$scope.toggleStdoutFullscreen = function() {
|
||||||
|
$scope.stdoutFullScreen = !$scope.stdoutFullScreen;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteJob = function() {
|
||||||
|
workflowResultsService.deleteJob($scope.workflow);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancelJob = function() {
|
||||||
|
workflowResultsService.cancelJob($scope.workflow);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.relaunchJob = function() {
|
||||||
|
workflowResultsService.relaunchJob($scope);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.stdoutArr = [];
|
||||||
|
|
||||||
|
// EVENT STUFF BELOW
|
||||||
|
|
||||||
|
// just putting the event queue on scope so it can be inspected in the
|
||||||
|
// console
|
||||||
|
// $scope.event_queue = eventQueue.queue;
|
||||||
|
// $scope.defersArr = eventQueue.populateDefers;
|
||||||
|
|
||||||
|
// This is where the async updates to the UI actually happen.
|
||||||
|
// Flow is event queue munging in the service -> $scope setting in here
|
||||||
|
var processEvent = function(event) {
|
||||||
|
// put the event in the queue
|
||||||
|
eventQueue.populate(event).then(mungedEvent => {
|
||||||
|
// make changes to ui based on the event returned from the queue
|
||||||
|
if (mungedEvent.changes) {
|
||||||
|
mungedEvent.changes.forEach(change => {
|
||||||
|
// we've got a change we need to make to the UI!
|
||||||
|
// update the necessary scope and make the change
|
||||||
|
if (change === 'startTime' && !$scope.workflow.start) {
|
||||||
|
$scope.workflow.start = mungedEvent.startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change === 'count' && !$scope.countFinished) {
|
||||||
|
// for all events that affect the host count,
|
||||||
|
// update the status bar as well as the host
|
||||||
|
// count badge
|
||||||
|
$scope.count = mungedEvent.count;
|
||||||
|
$scope.hostCount = getTotalHostCount(mungedEvent
|
||||||
|
.count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change === 'playCount') {
|
||||||
|
$scope.playCount = mungedEvent.playCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change === 'taskCount') {
|
||||||
|
$scope.taskCount = mungedEvent.taskCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change === 'finishedTime' && !$scope.workflow.finished) {
|
||||||
|
$scope.workflow.finished = mungedEvent.finishedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change === 'countFinished') {
|
||||||
|
// the playbook_on_stats event actually lets
|
||||||
|
// us know that we don't need to iteratively
|
||||||
|
// look at event to update the host counts
|
||||||
|
// any more.
|
||||||
|
$scope.countFinished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(change === 'stdout'){
|
||||||
|
angular
|
||||||
|
.element(".JobResultsStdOut-stdoutContainer")
|
||||||
|
.append($compile(mungedEvent
|
||||||
|
.stdout)($scope));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// the changes have been processed in the ui, mark it in the queue
|
||||||
|
eventQueue.markProcessed(event);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// PULL! grab completed event data and process each event
|
||||||
|
// TODO: implement retry logic in case one of these requests fails
|
||||||
|
// var getEvents = function(url) {
|
||||||
|
// workflowResultsService.getEvents(url)
|
||||||
|
// .then(events => {
|
||||||
|
// events.results.forEach(event => {
|
||||||
|
// // get the name in the same format as the data
|
||||||
|
// // coming over the websocket
|
||||||
|
// event.event_name = event.event;
|
||||||
|
// processEvent(event);
|
||||||
|
// });
|
||||||
|
// if (events.next) {
|
||||||
|
// getEvents(events.next);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
// getEvents($scope.job.related.job_events);
|
||||||
|
|
||||||
|
// Processing of job_events messages from the websocket
|
||||||
|
$scope.$on(`ws-job_events-${$scope.workflow.id}`, function(e, data) {
|
||||||
|
processEvent(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Processing of job-status messages from the websocket
|
||||||
|
$scope.$on(`ws-jobs`, function(e, data) {
|
||||||
|
if (parseInt(data.unified_job_id, 10) === parseInt($scope.workflow.id,10)) {
|
||||||
|
$scope.workflow.status = data.status;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<div class="tab-pane" id="job-results">
|
<div class="tab-pane" id="workflow-results">
|
||||||
<div ng-cloak
|
<div ng-cloak
|
||||||
id="htmlTemplate"
|
id="htmlTemplate"
|
||||||
class="JobResults"
|
class="WorkflowResults"
|
||||||
ng-class="{'fullscreen': stdoutFullScreen}">
|
ng-class="{'fullscreen': stdoutFullScreen}">
|
||||||
<div ui-view></div>
|
<div ui-view></div>
|
||||||
|
|
||||||
<!-- LEFT PANE -->
|
<!-- LEFT PANE -->
|
||||||
<div class="JobResults-leftSide"
|
<div class="WorkflowResults-leftSide"
|
||||||
ng-class="{'JobResults-stdoutActionButton--active': stdoutFullScreen}">
|
ng-class="{'WorkflowResults-stdoutActionButton--active': stdoutFullScreen}">
|
||||||
<div class="Panel"
|
<div class="Panel"
|
||||||
ng-show="!stdoutFullScreen">
|
ng-show="!stdoutFullScreen">
|
||||||
|
|
||||||
<!-- LEFT PANE HEADER -->
|
<!-- LEFT PANE HEADER -->
|
||||||
<div class="JobResults-panelHeader">
|
<div class="WorkflowResults-panelHeader">
|
||||||
<div
|
<div
|
||||||
class="JobResults-panelHeaderText">
|
class="WorkflowResults-panelHeaderText">
|
||||||
RESULTS
|
RESULTS
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
List-actionButton--delete"
|
List-actionButton--delete"
|
||||||
data-placement="top"
|
data-placement="top"
|
||||||
ng-click="deleteJob()"
|
ng-click="deleteJob()"
|
||||||
ng-show="job_status.status == 'running' ||
|
ng-show="workflow_status.status == 'running' ||
|
||||||
job_status.status=='pending' "
|
job_status.status=='pending' "
|
||||||
aw-tool-tip="Cancel"
|
aw-tool-tip="Cancel"
|
||||||
data-original-title="" title="">
|
data-original-title="" title="">
|
||||||
@@ -63,237 +63,97 @@
|
|||||||
<div>
|
<div>
|
||||||
|
|
||||||
<!-- START TIME DETAIL -->
|
<!-- START TIME DETAIL -->
|
||||||
<div class="JobResults-resultRow"
|
<div class="WorkflowResults-resultRow"
|
||||||
ng-show="job.started">
|
ng-show="workflow.started">
|
||||||
<label class="JobResults-resultRowLabel">
|
<label class="WorkflowResults-resultRowLabel">
|
||||||
Started
|
Started
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText">
|
<div class="WorkflowResults-resultRowText">
|
||||||
{{ job.started | longDate }}
|
{{ workflow.started | longDate }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- FINISHED TIME DETAIL -->
|
<!-- FINISHED TIME DETAIL -->
|
||||||
<div class="JobResults-resultRow"
|
<div class="WorkflowResults-resultRow"
|
||||||
ng-show="job.started">
|
ng-show="workflow.started">
|
||||||
<label class="JobResults-resultRowLabel">
|
<label class="WorkflowResults-resultRowLabel">
|
||||||
Finished
|
Finished
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText">
|
<div class="WorkflowResults-resultRowText">
|
||||||
{{ (job.finished |
|
{{ (workflow.finished |
|
||||||
longDate) || "Not Finished" }}
|
longDate) || "Not Finished" }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TEMPLATE DETAIL -->
|
<!-- TEMPLATE DETAIL -->
|
||||||
<div class="JobResults-resultRow"
|
<div class="WorkflowResults-resultRow"
|
||||||
ng-show="job.summary_fields.job_template.name">
|
ng-show="workflow.name">
|
||||||
<label class="JobResults-resultRowLabel">
|
<label class="WorkflowResults-resultRowLabel">
|
||||||
Template
|
Template
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText">
|
<div class="WorkflowResults-resultRowText">
|
||||||
<a href="{{ job_template_link }}"
|
<a href="{{ workflow_template_link }}"
|
||||||
aw-tool-tip="Edit the job template"
|
aw-tool-tip="Edit the job template"
|
||||||
data-placement="top">
|
data-placement="top">
|
||||||
{{ job.summary_fields.job_template.name }}
|
{{ workflow.name }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- JOB TYPE DETAIL -->
|
<!-- JOB TYPE DETAIL -->
|
||||||
<div class="JobResults-resultRow"
|
<div class="WorkflowResults-resultRow"
|
||||||
ng-show="job.job_type">
|
ng-show="workflow.type">
|
||||||
<label class="JobResults-resultRowLabel">
|
<label class="WorkflowResults-resultRowLabel">
|
||||||
Job Type
|
Job Type
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText">
|
<div class="WorkflowResults-resultRowText">
|
||||||
{{ type_label }}
|
Workflow Job
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CREATED BY DETAIL -->
|
<!-- CREATED BY DETAIL -->
|
||||||
<div class="JobResults-resultRow"
|
<div class="WorkflowResults-resultRow"
|
||||||
ng-show="job.summary_fields.created_by.username">
|
ng-show="workflow.summary_fields.created_by.username">
|
||||||
<label class="JobResults-resultRowLabel">
|
<label class="WorkflowResults-resultRowLabel">
|
||||||
Launched By
|
Launched By
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText">
|
<div class="WorkflowResults-resultRowText">
|
||||||
<a href="{{ created_by_link }}"
|
<a href="{{ created_by_link }}"
|
||||||
aw-tool-tip="Edit the User"
|
aw-tool-tip="Edit the User"
|
||||||
data-placement="top">
|
data-placement="top">
|
||||||
{{ job.summary_fields.created_by.username }}
|
{{ workflow.summary_fields.created_by.username }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- INVENTORY DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.summary_fields.inventory.name">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Inventory
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
<a href="{{ inventory_link }}"
|
|
||||||
aw-tool-tip="Edit the inventory"
|
|
||||||
data-placement="top">
|
|
||||||
{{ job.summary_fields.inventory.name }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- PROJECT DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.summary_fields.project.name">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Project
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
<a href="{{ project_link }}"
|
|
||||||
aw-tool-tip="Edit the project"
|
|
||||||
data-placement="top">
|
|
||||||
{{ job.summary_fields.project.name }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- PLAYBOOK DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.playbook">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Playbook
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
{{ job.playbook }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- MACHINE CREDENTIAL DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.summary_fields.credential.name">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Machine Credential
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
<a href="{{ machine_credential_link }}"
|
|
||||||
aw-tool-tip="Edit the credential"
|
|
||||||
data-placement="top">
|
|
||||||
{{ job.summary_fields.credential.name }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CLOUD CREDENTIAL DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.summary_fields.cloud_credential.name">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Cloud Credential
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
<a href="{{ cloud_credential_link }}"
|
|
||||||
aw-tool-tip="Edit the credential"
|
|
||||||
data-placement="top">
|
|
||||||
{{ job.summary_fields.cloud_credential.name }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- NETWORK CREDENTAIL DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.summary_fields.network_credential.name">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Network Credential
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
<a href="{{ network_credential_link }}"
|
|
||||||
aw-tool-tip="Edit the credential"
|
|
||||||
data-placement="top">
|
|
||||||
{{ job.summary_fields.network_credential.name }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- FORKS DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.forks !== undefined">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Forks
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
{{ job.forks }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- LIMIT DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.limit">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Limit
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
{{ job.limit }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- VERBOSITY DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.verbosity !== undefined">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Verbosity
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
{{ verbosity_label }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- TAGS DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.job_tags">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Job Tags
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
{{ job.job_tags }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- SKIP TAGS DETAIL -->
|
|
||||||
<div class="JobResults-resultRow"
|
|
||||||
ng-show="job.skip_tags">
|
|
||||||
<label class="JobResults-resultRowLabel">
|
|
||||||
Skip Tags
|
|
||||||
</label>
|
|
||||||
<div class="JobResults-resultRowText">
|
|
||||||
{{ job.skip_tags }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- EXTRA VARIABLES DETAIL -->
|
<!-- EXTRA VARIABLES DETAIL -->
|
||||||
<div class="JobResults-resultRow
|
<div class="WorkflowResults-resultRow
|
||||||
JobResults-resultRow--variables"
|
WorkflowResults-resultRow--variables"
|
||||||
ng-show="variables">
|
ng-show="variables">
|
||||||
<label class="JobResults-resultRowLabel
|
<label class="WorkflowResults-resultRowLabel
|
||||||
JobResults-resultRowLabel--fullWidth">
|
WorkflowResults-resultRowLabel--fullWidth">
|
||||||
Extra Variables
|
Extra Variables
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
rows="6"
|
rows="6"
|
||||||
ng-model="variables"
|
ng-model="variables"
|
||||||
name="variables"
|
name="variables"
|
||||||
class="JobResults-extraVars"
|
class="WorkflowResults-extraVars"
|
||||||
id="pre-formatted-variables">
|
id="pre-formatted-variables">
|
||||||
</textarea>
|
</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- LABELS DETAIL -->
|
<!-- LABELS DETAIL -->
|
||||||
<div class="JobResults-resultRow"
|
<div class="WorkflowResults-resultRow"
|
||||||
ng-show="labels && labels.length > 0">
|
ng-show="labels && labels.length > 0">
|
||||||
<label class="JobResults-resultRowLabel
|
<label class="WorkflowResults-resultRowLabel
|
||||||
JobResults-resultRowLabel--fullWidth">
|
WorkflowResults-resultRowLabel--fullWidth">
|
||||||
Labels
|
Labels
|
||||||
</label>
|
</label>
|
||||||
<div class="LabelList
|
<div class="LabelList
|
||||||
JobResults-resultRowText
|
WorkflowResults-resultRowText
|
||||||
JobResults-resultRowText--fullWidth">
|
WorkflowResults-resultRowText--fullWidth">
|
||||||
<div ng-repeat="label in labels"
|
<div ng-repeat="label in labels"
|
||||||
class="LabelList-tagContainer">
|
class="LabelList-tagContainer">
|
||||||
<div class="LabelList-tag">
|
<div class="LabelList-tag">
|
||||||
@@ -308,19 +168,19 @@
|
|||||||
<!-- STATUS DETAIL -->
|
<!-- STATUS DETAIL -->
|
||||||
<!-- <div
|
<!-- <div
|
||||||
class="form-group
|
class="form-group
|
||||||
JobResults-resultRow
|
WorkflowResults-resultRow
|
||||||
toggle-show">
|
toggle-show">
|
||||||
<label
|
<label
|
||||||
class="JobResults-resultRowLabel
|
class="WorkflowResults-resultRowLabel
|
||||||
col-lg-2 col-md-2
|
col-lg-2 col-md-2
|
||||||
col-sm-2 col-xs-3
|
col-sm-2 col-xs-3
|
||||||
control-label">
|
control-label">
|
||||||
Status
|
Status
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText
|
<div class="WorkflowResults-resultRowText
|
||||||
col-lg-10 col-md-10 col-sm-10 col-xs-9">
|
col-lg-10 col-md-10 col-sm-10 col-xs-9">
|
||||||
<i
|
<i
|
||||||
class="JobResults-statusIcon--results
|
class="WorkflowResults-statusIcon--results
|
||||||
fa
|
fa
|
||||||
icon-job-{{ job.status }}">
|
icon-job-{{ job.status }}">
|
||||||
</i> {{ status_label }}
|
</i> {{ status_label }}
|
||||||
@@ -330,16 +190,16 @@
|
|||||||
<!-- SCHEDULED BY DETAIL -->
|
<!-- SCHEDULED BY DETAIL -->
|
||||||
<!-- <div
|
<!-- <div
|
||||||
class="form-group
|
class="form-group
|
||||||
JobResults-resultRow toggle-show"
|
WorkflowResults-resultRow toggle-show"
|
||||||
ng-show="job.summary_fields.schedule_by.username">
|
ng-show="workflow.summary_fields.schedule_by.username">
|
||||||
<label
|
<label
|
||||||
class="JobResults-resultRowLabel
|
class="WorkflowResults-resultRowLabel
|
||||||
col-lg-2 col-md-2
|
col-lg-2 col-md-2
|
||||||
col-sm-2 col-xs-3
|
col-sm-2 col-xs-3
|
||||||
control-label">
|
control-label">
|
||||||
Launched By
|
Launched By
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText">
|
<div class="WorkflowResults-resultRowText">
|
||||||
<a href="{{ scheduled_by_link }}"
|
<a href="{{ scheduled_by_link }}"
|
||||||
aw-tool-tip="Edit the Schedule"
|
aw-tool-tip="Edit the Schedule"
|
||||||
data-placement="top">
|
data-placement="top">
|
||||||
@@ -351,16 +211,16 @@
|
|||||||
<!-- ELAPSED TIME DETAIL -->
|
<!-- ELAPSED TIME DETAIL -->
|
||||||
<!-- <div
|
<!-- <div
|
||||||
class="form-group
|
class="form-group
|
||||||
JobResults-resultRow toggle-show"
|
WorkflowResults-resultRow toggle-show"
|
||||||
ng-show="job_status.started">
|
ng-show="workflow_status.started">
|
||||||
<label
|
<label
|
||||||
class="JobResults-resultRowLabel
|
class="WorkflowResults-resultRowLabel
|
||||||
col-lg-2 col-md-2
|
col-lg-2 col-md-2
|
||||||
col-sm-2 col-xs-3
|
col-sm-2 col-xs-3
|
||||||
control-label">
|
control-label">
|
||||||
Elapsed
|
Elapsed
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText">
|
<div class="WorkflowResults-resultRowText">
|
||||||
{{ job_status.elapsed }}
|
{{ job_status.elapsed }}
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
@@ -368,11 +228,11 @@
|
|||||||
<!-- EXPLANATION DETAIL -->
|
<!-- EXPLANATION DETAIL -->
|
||||||
<!-- <div
|
<!-- <div
|
||||||
class="form-group
|
class="form-group
|
||||||
JobResults-resultRow
|
WorkflowResults-resultRow
|
||||||
toggle-show"
|
toggle-show"
|
||||||
ng-show="job_status.explanation">
|
ng-show="workflow_status.explanation">
|
||||||
<label
|
<label
|
||||||
class="JobResults-resultRowLabel
|
class="WorkflowResults-resultRowLabel
|
||||||
col-lg-2 col-md-2
|
col-lg-2 col-md-2
|
||||||
col-sm-2 col-xs-3
|
col-sm-2 col-xs-3
|
||||||
control-label">
|
control-label">
|
||||||
@@ -380,20 +240,20 @@
|
|||||||
</label> -->
|
</label> -->
|
||||||
|
|
||||||
<!-- PREVIOUS TASK SUCCEEDED -->
|
<!-- PREVIOUS TASK SUCCEEDED -->
|
||||||
<!-- <div class="JobResults-resultRowText
|
<!-- <div class="WorkflowResults-resultRowText
|
||||||
col-lg-10 col-md-10 col-sm-10 col-xs-9
|
col-lg-10 col-md-10 col-sm-10 col-xs-9
|
||||||
job_status_explanation"
|
job_status_explanation"
|
||||||
ng-show="!previousTaskFailed"
|
ng-show="!previousTaskFailed"
|
||||||
ng-bind-html="job_status.explanation">
|
ng-bind-html="job_status.explanation">
|
||||||
<i
|
<i
|
||||||
class="JobResults-statusIcon--results
|
class="WorkflowResults-statusIcon--results
|
||||||
fa
|
fa
|
||||||
icon-job-{{ job_status.status }}">
|
icon-job-{{ job_status.status }}">
|
||||||
</i> {{ job_status.status_label }}
|
</i> {{ job_status.status_label }}
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<!-- PREVIOUS TASK FAILED -->
|
<!-- PREVIOUS TASK FAILED -->
|
||||||
<!-- <div class="JobResults-resultRowText
|
<!-- <div class="WorkflowResults-resultRowText
|
||||||
col-lg-10 col-md-10 col-sm-10 col-xs-9
|
col-lg-10 col-md-10 col-sm-10 col-xs-9
|
||||||
job_status_explanation"
|
job_status_explanation"
|
||||||
ng-show="previousTaskFailed">
|
ng-show="previousTaskFailed">
|
||||||
@@ -419,15 +279,15 @@
|
|||||||
<!-- RESULTS TRACEBACK DETAIL -->
|
<!-- RESULTS TRACEBACK DETAIL -->
|
||||||
<!-- <div
|
<!-- <div
|
||||||
class="form-group
|
class="form-group
|
||||||
JobResults-resultRow
|
WorkflowResults-resultRow
|
||||||
toggle-show" ng-show="job.result_traceback">
|
toggle-show" ng-show="workflow.result_traceback">
|
||||||
<label
|
<label
|
||||||
class="JobResults-resultRowLabel
|
class="WorkflowResults-resultRowLabel
|
||||||
col-lg-2 col-md-12
|
col-lg-2 col-md-12
|
||||||
col-sm-12 col-xs-12">
|
col-sm-12 col-xs-12">
|
||||||
Results Traceback
|
Results Traceback
|
||||||
</label>
|
</label>
|
||||||
<div class="JobResults-resultRowText
|
<div class="WorkflowResults-resultRowText
|
||||||
col-lg-10 col-md-12 col-sm-12 col-xs-12
|
col-lg-10 col-md-12 col-sm-12 col-xs-12
|
||||||
job_status_traceback"
|
job_status_traceback"
|
||||||
ng-bind-html="job.result_traceback">
|
ng-bind-html="job.result_traceback">
|
||||||
@@ -440,50 +300,34 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- RIGHT PANE -->
|
<!-- RIGHT PANE -->
|
||||||
<div class="JobResults-rightSide">
|
<div class="WorkflowResults-rightSide">
|
||||||
<div class="Panel">
|
<div class="Panel">
|
||||||
|
|
||||||
<!-- RIGHT PANE HEADER -->
|
<!-- RIGHT PANE HEADER -->
|
||||||
<div class="StandardOut-panelHeader">
|
<div class="StandardOut-panelHeader">
|
||||||
<div class="StandardOut-panelHeaderText">
|
<div class="StandardOut-panelHeaderText">
|
||||||
<i class="JobResults-statusResultIcon
|
<i class="WorkflowResults-statusResultIcon
|
||||||
fa icon-job-{{ job.status }}">
|
fa icon-job-{{ job.status }}">
|
||||||
</i>
|
</i>
|
||||||
{{ job.name }}
|
{{ workflow.name }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- HEADER COUNTS -->
|
<!-- HEADER COUNTS -->
|
||||||
<div class="JobResults-badgeRow">
|
<div class="WorkflowResults-badgeRow">
|
||||||
<!-- PLAYS COUNT -->
|
<!-- PLAYS COUNT -->
|
||||||
<div class="JobResults-badgeTitle">
|
<div class="WorkflowResults-badgeTitle">
|
||||||
Plays
|
Total Jobs
|
||||||
</div>
|
</div>
|
||||||
<span class="badge List-titleBadge">
|
<span class="badge List-titleBadge">
|
||||||
{{ playCount || 0}}
|
{{ workflow_nodes.count || 0}}
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- TASKS COUNT -->
|
|
||||||
<div class="JobResults-badgeTitle">
|
|
||||||
Tasks
|
|
||||||
</div>
|
|
||||||
<span class="badge List-titleBadge">
|
|
||||||
{{ taskCount || 0}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- HOSTS COUNT -->
|
|
||||||
<div class="JobResults-badgeTitle">
|
|
||||||
Hosts
|
|
||||||
</div>
|
|
||||||
<span class="badge List-titleBadge">
|
|
||||||
{{ hostCount || 0}}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- ELAPSED TIME -->
|
<!-- ELAPSED TIME -->
|
||||||
<div class="JobResults-badgeTitle">
|
<div class="WorkflowResults-badgeTitle">
|
||||||
Elapsed
|
Elapsed
|
||||||
</div>
|
</div>
|
||||||
<span class="badge List-titleBadge">
|
<span class="badge List-titleBadge">
|
||||||
{{ job.elapsed * 1000 | duration: "hh:mm:ss" }}
|
{{ job.elapsed * 1000 }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -500,7 +344,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- DOWNLOAD ACTION -->
|
<!-- DOWNLOAD ACTION -->
|
||||||
<a ng-show="job_status.status === 'failed' ||
|
<a ng-show="workflow_status.status === 'failed' ||
|
||||||
job_status.status === 'successful' ||
|
job_status.status === 'successful' ||
|
||||||
job_status.status === 'canceled'"
|
job_status.status === 'canceled'"
|
||||||
href="/api/v1/jobs/{{ job.id }}/stdout?format=txt_download&token={{ token }}">
|
href="/api/v1/jobs/{{ job.id }}/stdout?format=txt_download&token={{ token }}">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import workflowResultsController from './workflow-results.controller';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'workflowResults',
|
name: 'workflowResults',
|
||||||
url: '/jobs/:id',
|
url: '/workflows/:id',
|
||||||
ncyBreadcrumb: {
|
ncyBreadcrumb: {
|
||||||
parent: 'jobs',
|
parent: 'jobs',
|
||||||
label: '{{ job.id }} - {{ job.name }}'
|
label: '{{ job.id }} - {{ job.name }}'
|
||||||
@@ -19,135 +19,95 @@ export default {
|
|||||||
socket: {
|
socket: {
|
||||||
"groups":{
|
"groups":{
|
||||||
"jobs": ["status_changed", "summary"],
|
"jobs": ["status_changed", "summary"],
|
||||||
"job_events": []
|
// not sure if you're gonna need to use job_events
|
||||||
|
// or if y'all will come up w/ a new socket group specifically
|
||||||
|
// for workflows
|
||||||
|
// "job_events": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
templateUrl: templateUrl('workflow-results/workflow-results'),
|
templateUrl: templateUrl('workflow-results/workflow-results'),
|
||||||
controller: workflowResultsController
|
controller: workflowResultsController,
|
||||||
// resolve: {
|
resolve: {
|
||||||
// // the GET for the particular job
|
// the GET for the particular workflow
|
||||||
// jobData: ['Rest', 'GetBasePath', '$stateParams', '$q', '$state', 'Alert', function(Rest, GetBasePath, $stateParams, $q, $state, Alert) {
|
workflowData: ['Rest', 'GetBasePath', '$stateParams', '$q', '$state', 'Alert', function(Rest, GetBasePath, $stateParams, $q, $state, Alert) {
|
||||||
// Rest.setUrl(GetBasePath('jobs') + $stateParams.id);
|
Rest.setUrl(GetBasePath('workflow_jobs') + $stateParams.id);
|
||||||
// var val = $q.defer();
|
var defer = $q.defer();
|
||||||
// Rest.get()
|
Rest.get()
|
||||||
// .then(function(data) {
|
.then(function(data) {
|
||||||
// val.resolve(data.data);
|
defer.resolve(data.data);
|
||||||
// }, function(data) {
|
}, function(data) {
|
||||||
// val.reject(data);
|
defer.reject(data);
|
||||||
//
|
|
||||||
// if (data.status === 404) {
|
if (data.status === 404) {
|
||||||
// Alert('Job Not Found', 'Cannot find job.', 'alert-info');
|
Alert('Job Not Found', 'Cannot find job.', 'alert-info');
|
||||||
// } else if (data.status === 403) {
|
} else if (data.status === 403) {
|
||||||
// Alert('Insufficient Permissions', 'You do not have permission to view this job.', 'alert-info');
|
Alert('Insufficient Permissions', 'You do not have permission to view this job.', 'alert-info');
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// $state.go('jobs');
|
$state.go('jobs');
|
||||||
// });
|
});
|
||||||
// return val.promise;
|
return defer.promise;
|
||||||
// }],
|
}],
|
||||||
// // after the GET for the job, this helps us keep the status bar from
|
// after the GET for the job, this helps us keep the status bar from
|
||||||
// // flashing as rest data comes in. If the job is finished and
|
// flashing as rest data comes in. Provides the list of workflow nodes
|
||||||
// // there's a playbook_on_stats event, go ahead and resolve the count
|
workflowNodes: ['workflowData', 'Rest', '$q', function(workflowData, Rest, $q) {
|
||||||
// // so you don't get that flashing!
|
var defer = $q.defer();
|
||||||
// count: ['jobData', 'jobResultsService', 'Rest', '$q', function(jobData, jobResultsService, Rest, $q) {
|
Rest.setUrl(workflowData.related.workflow_nodes);
|
||||||
// var defer = $q.defer();
|
Rest.get()
|
||||||
// if (jobData.finished) {
|
.success(function(data) {
|
||||||
// // if the job is finished, grab the playbook_on_stats
|
defer.resolve(data.data);
|
||||||
// // role to get the final count
|
})
|
||||||
// Rest.setUrl(jobData.related.job_events +
|
.error(function() {
|
||||||
// "?event=playbook_on_stats");
|
defer.resolve(data);
|
||||||
// Rest.get()
|
});
|
||||||
// .success(function(data) {
|
return defer.promise;
|
||||||
// if(!data.results[0]){
|
}],
|
||||||
// defer.resolve({val: {
|
// GET for the particular jobs labels to be displayed in the
|
||||||
// ok: 0,
|
// left-hand pane
|
||||||
// skipped: 0,
|
jobLabels: ['Rest', 'GetBasePath', '$stateParams', '$q', function(Rest, GetBasePath, $stateParams, $q) {
|
||||||
// unreachable: 0,
|
var getNext = function(data, arr, resolve) {
|
||||||
// failures: 0,
|
Rest.setUrl(data.next);
|
||||||
// changed: 0
|
Rest.get()
|
||||||
// }, countFinished: false});
|
.success(function (data) {
|
||||||
// }
|
if (data.next) {
|
||||||
// else {
|
getNext(data, arr.concat(data.results), resolve);
|
||||||
// defer.resolve({
|
} else {
|
||||||
// val: jobResultsService
|
resolve.resolve(arr.concat(data.results)
|
||||||
// .getCountsFromStatsEvent(data
|
.map(val => val.name));
|
||||||
// .results[0].event_data),
|
}
|
||||||
// countFinished: true});
|
});
|
||||||
// }
|
};
|
||||||
// })
|
|
||||||
// .error(function() {
|
var seeMoreResolve = $q.defer();
|
||||||
// defer.resolve({val: {
|
|
||||||
// ok: 0,
|
Rest.setUrl(GetBasePath('workflow_jobs') + $stateParams.id + '/labels/');
|
||||||
// skipped: 0,
|
Rest.get()
|
||||||
// unreachable: 0,
|
.success(function(data) {
|
||||||
// failures: 0,
|
if (data.next) {
|
||||||
// changed: 0
|
getNext(data, data.results, seeMoreResolve);
|
||||||
// }, countFinished: false});
|
} else {
|
||||||
// });
|
seeMoreResolve.resolve(data.results
|
||||||
// } else {
|
.map(val => val.name));
|
||||||
// // job isn't finished so just send an empty count and read
|
}
|
||||||
// // from events
|
});
|
||||||
// defer.resolve({val: {
|
|
||||||
// ok: 0,
|
return seeMoreResolve.promise;
|
||||||
// skipped: 0,
|
}],
|
||||||
// unreachable: 0,
|
// OPTIONS request for the workflow. Used to make things like the
|
||||||
// failures: 0,
|
// verbosity data in the left-hand pane prettier than just an
|
||||||
// changed: 0
|
// integer
|
||||||
// }, countFinished: false});
|
workflowDataOptions: ['Rest', 'GetBasePath', '$stateParams', '$q', function(Rest, GetBasePath, $stateParams, $q) {
|
||||||
// }
|
Rest.setUrl(GetBasePath('workflow_jobs') + $stateParams.id);
|
||||||
// return defer.promise;
|
var defer = $q.defer();
|
||||||
// }],
|
Rest.options()
|
||||||
// // GET for the particular jobs labels to be displayed in the
|
.then(function(data) {
|
||||||
// // left-hand pane
|
defer.resolve(data.data);
|
||||||
// jobLabels: ['Rest', 'GetBasePath', '$stateParams', '$q', function(Rest, GetBasePath, $stateParams, $q) {
|
}, function(data) {
|
||||||
// var getNext = function(data, arr, resolve) {
|
defer.reject(data);
|
||||||
// Rest.setUrl(data.next);
|
});
|
||||||
// Rest.get()
|
return defer.promise;
|
||||||
// .success(function (data) {
|
}]
|
||||||
// if (data.next) {
|
}
|
||||||
// getNext(data, arr.concat(data.results), resolve);
|
|
||||||
// } else {
|
|
||||||
// resolve.resolve(arr.concat(data.results)
|
|
||||||
// .map(val => val.name));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// var seeMoreResolve = $q.defer();
|
|
||||||
//
|
|
||||||
// Rest.setUrl(GetBasePath('jobs') + $stateParams.id + '/labels/');
|
|
||||||
// Rest.get()
|
|
||||||
// .success(function(data) {
|
|
||||||
// if (data.next) {
|
|
||||||
// getNext(data, data.results, seeMoreResolve);
|
|
||||||
// } else {
|
|
||||||
// seeMoreResolve.resolve(data.results
|
|
||||||
// .map(val => val.name));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// return seeMoreResolve.promise;
|
|
||||||
// }],
|
|
||||||
// // OPTIONS request for the job. Used to make things like the
|
|
||||||
// // verbosity data in the left-hand pane prettier than just an
|
|
||||||
// // integer
|
|
||||||
// jobDataOptions: ['Rest', 'GetBasePath', '$stateParams', '$q', function(Rest, GetBasePath, $stateParams, $q) {
|
|
||||||
// Rest.setUrl(GetBasePath('jobs') + $stateParams.id);
|
|
||||||
// var val = $q.defer();
|
|
||||||
// Rest.options()
|
|
||||||
// .then(function(data) {
|
|
||||||
// val.resolve(data.data);
|
|
||||||
// }, function(data) {
|
|
||||||
// val.reject(data);
|
|
||||||
// });
|
|
||||||
// return val.promise;
|
|
||||||
// }],
|
|
||||||
// // This clears out the event queue, otherwise it'd be full of events
|
|
||||||
// // for previous job results the user had navigated to
|
|
||||||
// eventQueueInit: ['eventQueue', function(eventQueue) {
|
|
||||||
// eventQueue.initialize();
|
|
||||||
// }]
|
|
||||||
// },
|
|
||||||
//
|
|
||||||
};
|
};
|
||||||
|
|||||||
196
awx/ui/client/src/workflow-results/workflow-results.service.js
Normal file
196
awx/ui/client/src/workflow-results/workflow-results.service.js
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun) {
|
||||||
|
var val = {
|
||||||
|
// the playbook_on_stats event returns the count data in a weird format.
|
||||||
|
// format to what we need!
|
||||||
|
getCountsFromStatsEvent: function(event_data) {
|
||||||
|
var hosts = {},
|
||||||
|
hostsArr;
|
||||||
|
|
||||||
|
// iterate over the event_data and populate an object with hosts
|
||||||
|
// and their status data
|
||||||
|
Object.keys(event_data).forEach(key => {
|
||||||
|
// failed passes boolean not integer
|
||||||
|
if (key === "failed") {
|
||||||
|
// array of hosts from failed type
|
||||||
|
hostsArr = Object.keys(event_data[key]);
|
||||||
|
hostsArr.forEach(host => {
|
||||||
|
if (!hosts[host]) {
|
||||||
|
// host has not been added to hosts object
|
||||||
|
// add now
|
||||||
|
hosts[host] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts[host][key] = event_data[key][host];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// array of hosts from each type ("changed", "dark", etc.)
|
||||||
|
hostsArr = Object.keys(event_data[key]);
|
||||||
|
hostsArr.forEach(host => {
|
||||||
|
if (!hosts[host]) {
|
||||||
|
// host has not been added to hosts object
|
||||||
|
// add now
|
||||||
|
hosts[host] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hosts[host][key]) {
|
||||||
|
// host doesn't have key
|
||||||
|
hosts[host][key] = 0;
|
||||||
|
}
|
||||||
|
hosts[host][key] += event_data[key][host];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// use the hosts data populate above to get the count
|
||||||
|
var count = {
|
||||||
|
ok : _.filter(hosts, function(o){
|
||||||
|
return !o.failures && !o.changed && 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;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// turn the count into an actual count, rather than a list of host
|
||||||
|
// names
|
||||||
|
Object.keys(count).forEach(key => {
|
||||||
|
count[key] = count[key].length;
|
||||||
|
});
|
||||||
|
|
||||||
|
return count;
|
||||||
|
},
|
||||||
|
getEvents: function(url) {
|
||||||
|
var val = $q.defer();
|
||||||
|
|
||||||
|
Rest.setUrl(url);
|
||||||
|
Rest.get()
|
||||||
|
.success(function(data) {
|
||||||
|
val.resolve({results: data.results,
|
||||||
|
next: data.next});
|
||||||
|
})
|
||||||
|
.error(function(obj, status) {
|
||||||
|
ProcessErrors(null, obj, status, null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: `Could not get job events.
|
||||||
|
Returned status: ${status}`
|
||||||
|
});
|
||||||
|
val.reject(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
return val.promise;
|
||||||
|
},
|
||||||
|
deleteJob: function(job) {
|
||||||
|
Prompt({
|
||||||
|
hdr: 'Delete Job',
|
||||||
|
body: `<div class='Prompt-bodyQuery'>
|
||||||
|
Are you sure you want to delete the job below?
|
||||||
|
</div>
|
||||||
|
<div class='Prompt-bodyTarget'>
|
||||||
|
#${job.id} ${$filter('sanitize')(job.name)}
|
||||||
|
</div>`,
|
||||||
|
action: function() {
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(job.url);
|
||||||
|
Rest.destroy()
|
||||||
|
.success(function() {
|
||||||
|
Wait('stop');
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
$state.go('jobs');
|
||||||
|
})
|
||||||
|
.error(function(obj, status) {
|
||||||
|
Wait('stop');
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
ProcessErrors(null, obj, status, null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: `Could not delete job.
|
||||||
|
Returned status: ${status}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
actionText: 'DELETE'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
cancelJob: function(job) {
|
||||||
|
var doCancel = function() {
|
||||||
|
Rest.setUrl(job.url + 'cancel');
|
||||||
|
Rest.post({})
|
||||||
|
.success(function() {
|
||||||
|
Wait('stop');
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
})
|
||||||
|
.error(function(obj, status) {
|
||||||
|
Wait('stop');
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
ProcessErrors(null, obj, status, null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: `Could not cancel job.
|
||||||
|
Returned status: ${status}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Prompt({
|
||||||
|
hdr: 'Cancel Job',
|
||||||
|
body: `<div class='Prompt-bodyQuery'>
|
||||||
|
Are you sure you want to cancel the job below?
|
||||||
|
</div>
|
||||||
|
<div class='Prompt-bodyTarget'>
|
||||||
|
#${job.id} ${$filter('sanitize')(job.name)}
|
||||||
|
</div>`,
|
||||||
|
action: function() {
|
||||||
|
Wait('start');
|
||||||
|
Rest.setUrl(job.url + 'cancel');
|
||||||
|
Rest.get()
|
||||||
|
.success(function(data) {
|
||||||
|
if (data.can_cancel === true) {
|
||||||
|
doCancel();
|
||||||
|
} else {
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
ProcessErrors(null, data, null, null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: `Job has completed,
|
||||||
|
unabled to be canceled.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Rest.destroy()
|
||||||
|
.success(function() {
|
||||||
|
Wait('stop');
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
})
|
||||||
|
.error(function(obj, status) {
|
||||||
|
Wait('stop');
|
||||||
|
$('#prompt-modal').modal('hide');
|
||||||
|
ProcessErrors(null, obj, status, null, {
|
||||||
|
hdr: 'Error!',
|
||||||
|
msg: `Could not cancel job.
|
||||||
|
Returned status: ${status}`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
actionText: 'CANCEL'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
relaunchJob: function(scope) {
|
||||||
|
InitiatePlaybookRun({ scope: scope, id: scope.job.id,
|
||||||
|
relaunch: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return val;
|
||||||
|
}];
|
||||||
Reference in New Issue
Block a user