mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 01:17:37 -02:30
adding workflow status bar to workflow results
This commit is contained in:
@@ -4,13 +4,12 @@
|
|||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
|
import workflowStatusBar from './workflow-status-bar/main';
|
||||||
import route from './workflow-results.route.js';
|
import route from './workflow-results.route.js';
|
||||||
|
|
||||||
import workflowResultsService from './workflow-results.service';
|
import workflowResultsService from './workflow-results.service';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('workflowResults', [])
|
angular.module('workflowResults', [workflowStatusBar.name])
|
||||||
.run(['$stateExtender', function($stateExtender) {
|
.run(['$stateExtender', function($stateExtender) {
|
||||||
$stateExtender.addState(route);
|
$stateExtender.addState(route);
|
||||||
}])
|
}])
|
||||||
|
|||||||
@@ -164,136 +164,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- STATUS DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
WorkflowResults-resultRow
|
|
||||||
toggle-show">
|
|
||||||
<label
|
|
||||||
class="WorkflowResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-2
|
|
||||||
col-sm-2 col-xs-3
|
|
||||||
control-label">
|
|
||||||
Status
|
|
||||||
</label>
|
|
||||||
<div class="WorkflowResults-resultRowText
|
|
||||||
col-lg-10 col-md-10 col-sm-10 col-xs-9">
|
|
||||||
<i
|
|
||||||
class="WorkflowResults-statusIcon--results
|
|
||||||
fa
|
|
||||||
icon-job-{{ job.status }}">
|
|
||||||
</i> {{ status_label }}
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- SCHEDULED BY DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
WorkflowResults-resultRow toggle-show"
|
|
||||||
ng-show="workflow.summary_fields.schedule_by.username">
|
|
||||||
<label
|
|
||||||
class="WorkflowResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-2
|
|
||||||
col-sm-2 col-xs-3
|
|
||||||
control-label">
|
|
||||||
Launched By
|
|
||||||
</label>
|
|
||||||
<div class="WorkflowResults-resultRowText">
|
|
||||||
<a href="{{ scheduled_by_link }}"
|
|
||||||
aw-tool-tip="Edit the Schedule"
|
|
||||||
data-placement="top">
|
|
||||||
{{ job.summary_fields.scheduled_by.username }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- ELAPSED TIME DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
WorkflowResults-resultRow toggle-show"
|
|
||||||
ng-show="workflow_status.started">
|
|
||||||
<label
|
|
||||||
class="WorkflowResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-2
|
|
||||||
col-sm-2 col-xs-3
|
|
||||||
control-label">
|
|
||||||
Elapsed
|
|
||||||
</label>
|
|
||||||
<div class="WorkflowResults-resultRowText">
|
|
||||||
{{ job_status.elapsed }}
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- EXPLANATION DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
WorkflowResults-resultRow
|
|
||||||
toggle-show"
|
|
||||||
ng-show="workflow_status.explanation">
|
|
||||||
<label
|
|
||||||
class="WorkflowResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-2
|
|
||||||
col-sm-2 col-xs-3
|
|
||||||
control-label">
|
|
||||||
Explanation
|
|
||||||
</label> -->
|
|
||||||
|
|
||||||
<!-- PREVIOUS TASK SUCCEEDED -->
|
|
||||||
<!-- <div class="WorkflowResults-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">
|
|
||||||
<i
|
|
||||||
class="WorkflowResults-statusIcon--results
|
|
||||||
fa
|
|
||||||
icon-job-{{ job_status.status }}">
|
|
||||||
</i> {{ job_status.status_label }}
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!-- PREVIOUS TASK FAILED -->
|
|
||||||
<!-- <div class="WorkflowResults-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> -->
|
|
||||||
|
|
||||||
<!-- RESULTS TRACEBACK DETAIL -->
|
|
||||||
<!-- <div
|
|
||||||
class="form-group
|
|
||||||
WorkflowResults-resultRow
|
|
||||||
toggle-show" ng-show="workflow.result_traceback">
|
|
||||||
<label
|
|
||||||
class="WorkflowResults-resultRowLabel
|
|
||||||
col-lg-2 col-md-12
|
|
||||||
col-sm-12 col-xs-12">
|
|
||||||
Results Traceback
|
|
||||||
</label>
|
|
||||||
<div class="WorkflowResults-resultRowText
|
|
||||||
col-lg-10 col-md-12 col-sm-12 col-xs-12
|
|
||||||
job_status_traceback"
|
|
||||||
ng-bind-html="job.result_traceback">
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -319,7 +189,7 @@
|
|||||||
Total Jobs
|
Total Jobs
|
||||||
</div>
|
</div>
|
||||||
<span class="badge List-titleBadge">
|
<span class="badge List-titleBadge">
|
||||||
{{ workflow_nodes.count || 0}}
|
{{ workflow_nodes.length || 0}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- ELAPSED TIME -->
|
<!-- ELAPSED TIME -->
|
||||||
@@ -357,7 +227,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<host-status-bar></host-status-bar>
|
<workflow-status-bar></workflow-status-bar>
|
||||||
<!-- <job-results-standard-out></job-results-standard-out> -->
|
<!-- <job-results-standard-out></job-results-standard-out> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default {
|
|||||||
Rest.setUrl(workflowData.related.workflow_nodes);
|
Rest.setUrl(workflowData.related.workflow_nodes);
|
||||||
Rest.get()
|
Rest.get()
|
||||||
.success(function(data) {
|
.success(function(data) {
|
||||||
defer.resolve(data.data);
|
defer.resolve(data.results);
|
||||||
})
|
})
|
||||||
.error(function() {
|
.error(function() {
|
||||||
defer.resolve(data);
|
defer.resolve(data);
|
||||||
|
|||||||
@@ -7,102 +7,14 @@
|
|||||||
|
|
||||||
export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun) {
|
export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErrors', 'InitiatePlaybookRun', function ($q, Prompt, $filter, Wait, Rest, $state, ProcessErrors, InitiatePlaybookRun) {
|
||||||
var val = {
|
var val = {
|
||||||
// the playbook_on_stats event returns the count data in a weird format.
|
deleteJob: function(workflow) {
|
||||||
// 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({
|
Prompt({
|
||||||
hdr: 'Delete Job',
|
hdr: 'Delete Job',
|
||||||
body: `<div class='Prompt-bodyQuery'>
|
body: `<div class='Prompt-bodyQuery'>
|
||||||
Are you sure you want to delete the job below?
|
Are you sure you want to delete the workflow below?
|
||||||
</div>
|
</div>
|
||||||
<div class='Prompt-bodyTarget'>
|
<div class='Prompt-bodyTarget'>
|
||||||
#${job.id} ${$filter('sanitize')(job.name)}
|
#${workflow.id} ${$filter('sanitize')(workflow.name)}
|
||||||
</div>`,
|
</div>`,
|
||||||
action: function() {
|
action: function() {
|
||||||
Wait('start');
|
Wait('start');
|
||||||
@@ -126,9 +38,9 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr
|
|||||||
actionText: 'DELETE'
|
actionText: 'DELETE'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
cancelJob: function(job) {
|
cancelJob: function(workflow) {
|
||||||
var doCancel = function() {
|
var doCancel = function() {
|
||||||
Rest.setUrl(job.url + 'cancel');
|
Rest.setUrl(workflow.url + 'cancel');
|
||||||
Rest.post({})
|
Rest.post({})
|
||||||
.success(function() {
|
.success(function() {
|
||||||
Wait('stop');
|
Wait('stop');
|
||||||
@@ -139,23 +51,23 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr
|
|||||||
$('#prompt-modal').modal('hide');
|
$('#prompt-modal').modal('hide');
|
||||||
ProcessErrors(null, obj, status, null, {
|
ProcessErrors(null, obj, status, null, {
|
||||||
hdr: 'Error!',
|
hdr: 'Error!',
|
||||||
msg: `Could not cancel job.
|
msg: `Could not cancel workflow.
|
||||||
Returned status: ${status}`
|
Returned status: ${status}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Prompt({
|
Prompt({
|
||||||
hdr: 'Cancel Job',
|
hdr: 'Cancel Workflow',
|
||||||
body: `<div class='Prompt-bodyQuery'>
|
body: `<div class='Prompt-bodyQuery'>
|
||||||
Are you sure you want to cancel the job below?
|
Are you sure you want to cancel the workflow below?
|
||||||
</div>
|
</div>
|
||||||
<div class='Prompt-bodyTarget'>
|
<div class='Prompt-bodyTarget'>
|
||||||
#${job.id} ${$filter('sanitize')(job.name)}
|
#${workflow.id} ${$filter('sanitize')(workflow.name)}
|
||||||
</div>`,
|
</div>`,
|
||||||
action: function() {
|
action: function() {
|
||||||
Wait('start');
|
Wait('start');
|
||||||
Rest.setUrl(job.url + 'cancel');
|
Rest.setUrl(workflow.url + 'cancel');
|
||||||
Rest.get()
|
Rest.get()
|
||||||
.success(function(data) {
|
.success(function(data) {
|
||||||
if (data.can_cancel === true) {
|
if (data.can_cancel === true) {
|
||||||
@@ -179,7 +91,7 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr
|
|||||||
$('#prompt-modal').modal('hide');
|
$('#prompt-modal').modal('hide');
|
||||||
ProcessErrors(null, obj, status, null, {
|
ProcessErrors(null, obj, status, null, {
|
||||||
hdr: 'Error!',
|
hdr: 'Error!',
|
||||||
msg: `Could not cancel job.
|
msg: `Could not cancel workflow.
|
||||||
Returned status: ${status}`
|
Returned status: ${status}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -188,7 +100,7 @@ export default ['$q', 'Prompt', '$filter', 'Wait', 'Rest', '$state', 'ProcessErr
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
relaunchJob: function(scope) {
|
relaunchJob: function(scope) {
|
||||||
InitiatePlaybookRun({ scope: scope, id: scope.job.id,
|
InitiatePlaybookRun({ scope: scope, id: scope.workflow.id,
|
||||||
relaunch: true });
|
relaunch: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2015 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
import workflowStatusBar from './workflow-status-bar.directive';
|
||||||
|
|
||||||
|
export default
|
||||||
|
angular.module('workflowStatusBarDirective', [])
|
||||||
|
.directive('workflowStatusBar', workflowStatusBar);
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
@import '../../shared/branding/colors.default.less';
|
||||||
|
|
||||||
|
.WorkflowStatusBar {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-ok,
|
||||||
|
.WorkflowStatusBar-changed,
|
||||||
|
.WorkflowStatusBar-unreachable,
|
||||||
|
.WorkflowStatusBar-failures,
|
||||||
|
.WorkflowStatusBar-skipped,
|
||||||
|
.WorkflowStatusBar-noData {
|
||||||
|
height: 15px;
|
||||||
|
border-top: 5px solid @default-bg;
|
||||||
|
border-bottom: 5px solid @default-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-ok {
|
||||||
|
background-color: @default-succ;
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-changed {
|
||||||
|
background-color: @default-warning;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-unreachable {
|
||||||
|
background-color: @default-unreachable;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-failures {
|
||||||
|
background-color: @default-err;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-skipped {
|
||||||
|
background-color: @default-link;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-noData {
|
||||||
|
background-color: @default-icon-hov;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-tooltipLabel {
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-tooltipBadge {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-tooltipBadge--ok {
|
||||||
|
background-color: @default-succ;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-tooltipBadge--unreachable {
|
||||||
|
background-color: @default-unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-tooltipBadge--skipped {
|
||||||
|
background-color: @default-link;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-tooltipBadge--changed {
|
||||||
|
background-color: @default-warning;
|
||||||
|
}
|
||||||
|
|
||||||
|
.WorkflowStatusBar-tooltipBadge--failures {
|
||||||
|
background-color: @default-err;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*************************************************
|
||||||
|
* Copyright (c) 2016 Ansible, Inc.
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
// import WorkflowStatusBarController from './host-status-bar.controller';
|
||||||
|
export default [ 'templateUrl',
|
||||||
|
function(templateUrl) {
|
||||||
|
return {
|
||||||
|
scope: true,
|
||||||
|
templateUrl: templateUrl('workflow-results/workflow-status-bar/workflow-status-bar'),
|
||||||
|
restrict: 'E',
|
||||||
|
// controller: standardOutLogController,
|
||||||
|
link: function(scope) {
|
||||||
|
// as count is changed by event data coming in,
|
||||||
|
// update the host status bar
|
||||||
|
scope.$watch('count', function(val) {
|
||||||
|
if (val) {
|
||||||
|
Object.keys(val).forEach(key => {
|
||||||
|
// reposition the hosts status bar by setting
|
||||||
|
// the various flex values to the count of
|
||||||
|
// those hosts
|
||||||
|
$(`.WorkflowStatusBar-${key}`)
|
||||||
|
.css('flex', `${val[key]} 0 auto`);
|
||||||
|
|
||||||
|
// set the tooltip to give how many hosts of
|
||||||
|
// each type
|
||||||
|
if (val[key] > 0) {
|
||||||
|
scope[`${key}CountTip`] = `<span class='WorkflowStatusBar-tooltipLabel'>${key}</span><span class='badge WorkflowStatusBar-tooltipBadge WorkflowStatusBar-tooltipBadge--${key}'>${val[key]}</span>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// if there are any hosts that have finished, don't
|
||||||
|
// show default grey bar
|
||||||
|
scope.hostsFinished = (Object
|
||||||
|
.keys(val)
|
||||||
|
.filter(key => (val[key] > 0)).length > 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}];
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<div class="WorkflowStatusBar">
|
||||||
|
<div class="WorkflowStatusBar-ok"
|
||||||
|
data-placement="top"
|
||||||
|
aw-tool-tip="{{okCountTip}}"
|
||||||
|
data-tip-watch="okCountTip"></div>
|
||||||
|
<div class="WorkflowStatusBar-changed"
|
||||||
|
data-placement="top"
|
||||||
|
aw-tool-tip="{{changedCountTip}}"
|
||||||
|
data-tip-watch="changedCountTip"></div>
|
||||||
|
<div class="WorkflowStatusBar-failures"
|
||||||
|
data-placement="top"
|
||||||
|
aw-tool-tip="{{failuresCountTip}}"
|
||||||
|
data-tip-watch="failuresCountTip"></div>
|
||||||
|
<div class="WorkflowStatusBar-unreachable"
|
||||||
|
data-placement="top"
|
||||||
|
aw-tool-tip="{{unreachableCountTip}}"
|
||||||
|
data-tip-watch="unreachableCountTip"></div>
|
||||||
|
<div class="WorkflowStatusBar-skipped"
|
||||||
|
data-placement="top"
|
||||||
|
aw-tool-tip="{{skippedCountTip}}"
|
||||||
|
data-tip-watch="skippedCountTip"></div>
|
||||||
|
<div class="WorkflowStatusBar-noData"
|
||||||
|
aw-tool-tip="NO HOSTS FINISHED"
|
||||||
|
ng-hide="hostsFinished"
|
||||||
|
data-placement="top"></div>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user