Restyled new job detail page making it completely responsive down to small viewports. Moved things around as discussed in last status meeting. Incorporated event_name element from event object.

This commit is contained in:
Chris Houseknecht 2014-04-25 19:25:12 -04:00
parent f788adfbca
commit 4521f8f41a
5 changed files with 270 additions and 196 deletions

View File

@ -5,6 +5,8 @@
*
*/
/* global _ */
'use strict';
function JobDetailController ($scope, $compile, $routeParams, ClearScope, Breadcrumbs, LoadBreadCrumbs, GetBasePath, Wait, Rest, ProcessErrors, DigestEvents,
@ -76,6 +78,7 @@ function JobDetailController ($scope, $compile, $routeParams, ClearScope, Breadc
event_socket.on("job_events-" + job_id, function(data) {
var matches;
data.id = data.event_id;
data.event = data.event_name;
if (api_complete) {
matches = processed_events.find(function(x) { return x === data.id; });
if (matches.length === 0) {
@ -236,7 +239,7 @@ function JobDetailController ($scope, $compile, $routeParams, ClearScope, Breadc
});
};
$( "#hosts-slider-vertical" ).slider({
$("#hosts-slider-vertical").slider({
orientation: "vertical",
range: "min",
min: 0,
@ -246,6 +249,27 @@ function JobDetailController ($scope, $compile, $routeParams, ClearScope, Breadc
$( "#amount" ).val( ui.value );
}
});
// Use debounce from the underscore library. We're including underscore
// for the timezone stuff, so might as well take advantage of it.
function adjustSize() {
var ww = $(window).width();
if (ww < 1240) {
$('#job-summary-container').hide();
$('#job-detail-container').css({ "width": "100%", "padding-right": "15px" });
}
else {
$('#job-detail-container').css({ "width": "58.33333333%", "padding-right": "7px" });
$('#job-summary-container').show();
}
}
$(document).ready(function() {
adjustSize();
});
$(window).resize(_.debounce(function(){
adjustSize();
}, 500));
}
JobDetailController.$inject = [ '$scope', '$compile', '$routeParams', 'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'GetBasePath', 'Wait',

View File

@ -157,7 +157,8 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
status: 'unreachable',
event_id: event.id,
created: event.created,
modified: event.modified
modified: event.modified,
message: (event.event_data && event.event_data.res) ? event.event_data.res.msg : ''
});
}
@ -170,7 +171,8 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
status: 'failed',
event_id: event.id,
created: event.created,
modified: event.modified
modified: event.modified,
message: (event.event_data && event.event_data.res) ? event.event_data.res.msg : ''
});
}
if (event.event === 'runner_on_skipped') {
@ -182,7 +184,8 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
status: 'skipped',
event_id: event.id,
created: event.created,
modified: event.modified
modified: event.modified,
message: (event.event_data && event.event_data.res) ? event.event_data.res.msg : ''
});
}
if (event.event === 'runner_on_ok') {
@ -194,7 +197,8 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
status: ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ),
event_id: event.id,
created: event.created,
modified: event.modified
modified: event.modified,
message: (event.event_data && event.event_data.res) ? event.event_data.res.msg : ''
});
}
if (event.event === 'playbook_on_stats') {
@ -453,6 +457,7 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
task_id = params.task_id,
modified = params.modified,
created = params.created,
msg = params.message,
host_found = false;
scope.hosts.every(function(host, i) {
@ -493,7 +498,8 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
event_id: event_id,
status: status,
name: name,
created: created
created: created,
message: msg
});
};
}])
@ -508,6 +514,7 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
status = params.status,
created = params.created,
name = params.name,
msg = params.message,
play_id, first;
scope.hostResults.push({
@ -516,7 +523,8 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
host_id: host_id,
task_id: task_id,
name: name,
created: created
created: created,
msg: msg
});
scope.tasks.every(function(task) {
@ -587,12 +595,12 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
callback: function() {
// Scroll the task table all the way to the bottom, revealing the last row
setTimeout(function() {
var original_height = $('#task-table-body').css('height'),
var original_height = $('#tasks-table-detail').css('height'),
table_height;
$('#task-table-body').css('height', 'auto');
table_height = $('#task-table-body').height();
$('#task-table-body').css('height', original_height);
$('#task-table-body').scrollTop(table_height);
$('#tasks-table-detail').css('height', 'auto');
table_height = $('#tasks-table-detail').height();
$('#tasks-table-detail').css('height', original_height);
$('#tasks-table-detail').scrollTop(table_height);
}, 300);
}
});
@ -624,8 +632,8 @@ function(UpdatePlayStatus, UpdatePlayNoHostsMatched, UpdateHostStatus, UpdatePla
.factory('SelectHost', [ function() {
return function() {
setTimeout(function() {
var inner_height = $('#host_details .job-detail-table').height();
$('#host_details').scrollTop(inner_height);
var inner_height = $('#hosts-table-details').height();
$('#hosts-table-details').scrollTop(inner_height);
}, 100);
};
}]);

View File

@ -24,11 +24,12 @@ angular.module('JobTemplatesListDefinition', [])
fields: {
name: {
key: true,
label: 'Name'
label: 'Name',
columnClass: 'col-lg-5 col-md-5 col-sm-9 col-xs-8'
},
description: {
label: 'Description',
columnClass: 'hidden-sm hidden-xs'
columnClass: 'col-lg-4 col-md-4 hidden-sm hidden-xs'
}
},

View File

@ -19,58 +19,10 @@
}
#job-status {
margin: 8px 0 15px 0;
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
display: inline-block;
margin-right: 20px;
}
i {
font-size: 12px;
}
.label {
display: inline-block;
text-align: left;
width: 50px;
font-size: 12px;
color: @black;
padding-left: 0;
}
}
.job-detail-table {
margin-bottom: 0;
border: 1px solid @grey;
background-color: @white;
/* http://stackoverflow.com/questions/21168521/scrollable-table-with-fixed-header-in-bootstrap */
width: 100%;
thead, tbody, tr, td, th { display: block; }
tr:after {
content: ' ';
display: block;
visibility: hidden;
clear: both;
}
tbody {
overflow-y: auto;
height: 150px;
}
thead {
/* fallback */
}
thead>tr>th {
height: 22px;
}
tbody td, thead th {
height: auto;
float: left;
}
tbody>tr>td {
border-top-color: @well;
@ -85,22 +37,6 @@
tbody>tr.active, tbody>tr.active>td {
background-color: #EDF2F2;
}
.status-column i {
font-size: 12px;
}
}
.section {
margin-bottom: 20px;
h5 {
margin-top: 0;
margin-bottom: 12px;
}
}
.section:last-child {
margin-bottom: 0;
}
.job_summary {
@ -162,6 +98,33 @@
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
}
.section {
margin-bottom: 20px;
h5 {
margin-top: 0;
margin-bottom: 12px;
}
}
.section:last-child {
margin-bottom: 0;
}
#job-detail-tables {
margin-top: 20px;
}
.status-row {
margin-bottom: 10px;
.job-label {
font-weight: bold;
font-size: 12px;
}
}
#job_options {
height: 100px;
overflow-y: auto;
@ -174,30 +137,96 @@
overflow-x: none;
}
#hosts-section {
border: 1px solid @grey;
border-top: 2px solid #ddd;
padding: 5px;
height: 150px;
background-color: @white;
overflow-y: hide;
overflow-x: auto;
#breadcrumb-container {
padding-right: 10px;
}
#host-details {
ul {
list-style: none;
margin: 0;
padding: 0;
i {
font-size: 12px;
}
}
li {
border-bottom: 1px solid @well;
padding-bottom: 2px;
#job-detail-container {
position: relative;
padding-left: 15px;
padding-right: 7px;
width: 58.33333333%;
}
#job-summary-container {
position: absolute;
top: 0;
right: 0;
padding-right: 15px;
padding-left: 7px;
width: 41.66666667%;
}
.table-header {
font-weight: bold;
font-size: 12px;
}
.table-detail {
overflow-x: hidden;
overflow-y: auto;
border: 1px solid @black;
background-color: @white;
.row {
border-top: 1px solid @grey;
}
/*.row:nth-child(odd) {
background-color: @well;
padding-top: 0;
margin: 0;
line-height: normal;
padding-bottom: 0;
}*/
.active {
background-color: #EDF2F2;
}
}
.status-column i {
font-size: 12px;
}
#tasks-table-detail {
height: 150px;
}
#play-section {
.table-detail {
height: 150px;
}
}
#task-hosts-section {
position: relative;
top: 0;
left: 0;
#hosts-table-detail {
padding: 3px;
border: 1px solid @grey;
border-top: 2px solid #ddd;
height: 150px;
background-color: @white;
.col-lg-1, .col-md-1, .col-sm-1, .col-xs-1 {
width: 3%;
}
}
}
#hosts-summary-section {
.col-lg-4, .col-md-4, .col-sm-4, .col-xs-4 {
width: 33%;
}
.col-lg-2, .col-md-2, .col-sm-2, .col-xs-2 {
width: 16.5%;
}
.table-header {
font-size: 12px;
}
.table-detail {
height: 200px;
}
.name {
word-break: break-all;
}
}

View File

@ -2,98 +2,111 @@
<div ng-cloak id="htmlTemplate">
<div class="row">
<div class="col-md-12">
<div id="breadcrumb-container" class="col-md-12">
<div class="nav-path">
<ul class="breadcrumb" id="breadcrumb-list">
<li><a href="/#/jobs">Jobs</a></li>
<li><strong>{{ job_id }}</strong> - <a href="{{ job_template_url }}">{{ job_template_name }}</a> - <a href="{{ project_url }}">{{ project_name }}</a> - <a href="{{ inventory_url }}">{{ inventory_name }}</a></li>
<li><strong>{{ job_id }}</strong> - <a href="{{ job_template_url }}" aw-tool-tip="View the job template" data-placement="top">{{ job_template_name }}</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-7">
<div class="row" style="position: relative;">
<div id="job-detail-container">
<div class="job_well">
<div id="job-status">
<ul>
<li ng-show="job_status.explanation" style="display: block; margin-bottom: 10px;"><div ng-class="job_status.status_class"><i class="fa icon-job-{{ job_status.status }}"></i> {{ job_status.status }} &nbsp;-{{ job_status.explanation }}</div></li>
<li ng-show="!job_status.explanation"><div class="label">Status</div> <i class="fa icon-job-{{ job_status.status }}"></i> {{ job_status.status }}</li>
<li ng-show="job_status.started"><div class="label">Start</div> {{ job_status.started | date:'MM/dd/yy HH:mm:ss' }}</li>
<li ng-show="!job_status.started"><div class="label">Start</div> Not Started</li>
<li ng-show="job_status.finished"><div class="label">Finish</div> {{ job_status.finished | date:'MM/dd/yy HH:mm:ss' }}<li>
<li><div class="label">Elapsed</div> {{ job_status.elapsed }}</li>
</ul>
<div class="row status-row">
<div class="col-lg-1 col-md-1 col-sm-2 col-xs-2 job-label">Status</div>
<div class="col-lg-11 col-md-11 col-sm-10 col-xs-10 status-column"><i class="fa icon-job-{{ job_status.status }}"></i> {{ job_status.status }} {{ job_status.explanation }}</div>
</div>
<div class="job-detail-tables">
<div class="section">
<div class="row status-row">
<div class="col-lg-1 col-md-1 col-sm-2 col-xs-2 job-label">Started</div>
<div ng-show="job_status.started" class="col-lg-5 col-md-5 col-sm-4 col-xs-4">{{ job_status.started | date:'MM/dd/yy HH:mm:ss' }}</div>
<div ng-show="!job_status.started" class="col-lg-5 col-md-5 col-sm-4 col-xs-4">Not Started</div>
<div class="col-lg-1 col-md-1 col-sm-2 col-xs-2 job-label">Elapsed</div>
<div ng-show="job_status.finished" class="col-lg-5 col-md-5 col-sm-4 col-xs-4">{{ job_status.elapsed }}</div>
<div ng-show="!job_status.finished" class="col-lg-5 col-md-5 col-sm-4 col-xs-4">00:00:00</div>
</div>
<div class="row status-row">
<div class="col-lg-1 col-md-1 col-sm-2 col-xs-2 job-label">Project</div>
<div class="col-lg-5 col-md-5 col-sm-4 col-xs-4"><a href="{{ project_url }}" aw-tool-tip="View the project" data-placement="top">{{ project_name }}</a></div>
<div class="col-lg-1 col-md-1 col-sm-2 col-xs-2 job-label">Inventory</div>
<div class="col-lg-5 col-md-5 col-sm-4 col-xs-4"><a href="{{ inventory_url }}" aw-tool-tip="View the inventory" data-placement="top">{{ inventory_name }}</a></div>
</div>
<div id="job-detail-tables">
<div id="play-section" class="section">
<h5>Plays</h5>
<table class="table job-detail-table">
<thead>
<tr>
<th class="col-lg-1 col-md-1 col-sm-2 hidden-xs">Started</th>
<th class="col-lg-10 col-md-10 col-sm-10 col-xs-12">Name</th>
<th class="col-lg-1 col-md-1 hidden-sm hidden-xs">Elapsed</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="play in plays" ng-class="play.playActiveClass" ng-click="selectPlay(play.id)" class="cursor-pointer">
<td class="col-lg-1 col-md-1 col-sm-2 hidden-xs">{{ play.created | date: 'HH:mm:ss' }}</td>
<td class="col-lg-10 col-md-9 col-sm-10 col-xs-12 status-column">
<i class="fa icon-job-{{ play.status }}"></i> {{ play.name }}</span></td>
<td class="col-lg-1 col-md-1 col-hidden-sm hidden-xs" aw-tool-tip="Completed at {{ play.finished | date:'HH:mm:ss' }}"
data-placement="top">{{ play.elapsed }}</td>
</tr>
</tbody>
</table>
<div class="table-header">
<div class="row">
<div class="col-lg-1 col-md-1 col-sm-2 hidden-xs">Started</div>
<div class="col-lg-1 col-md-1 hidden-sm hidden-xs">Elapsed</div>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12">Name</div>
</div>
</div>
<div id="plays-table-detail" class="table-detail">
<div class="row cursor-pointer" ng-repeat="play in plays" ng-class="play.playActiveClass" ng-click="selectPlay(play.id)">
<div class="col-lg-1 col-md-1 col-sm-2 hidden-xs">{{ play.created | date: 'HH:mm:ss' }}</div>
<div class="col-lg-1 col-md-1 hidden-sm hidden-xs" aw-tool-tip="Completed at {{ play.finished | date:'HH:mm:ss' }}"
data-placement="top">{{ play.elapsed }}
</div>
<div class="col-lg-10 col-md-10 col-sm-10 col-xs-12 status-column">
<i class="fa icon-job-{{ play.status }}"></i> {{ play.name }}</span>
</div>
</div>
</div>
</div><!-- section -->
<div class="section">
<div id="task-section" class="section">
<h5>Tasks</h5>
<table class="table job-detail-table">
<thead>
<tr>
<th class="col-lg-1 col-md-1 col-sm-2 hidden-xs">Started</th>
<th class="col-lg-6 col-md-6 col-sm-6 col-xs-7">Name</th>
<th class="col-lg-4 col-md-4 col-sm-4 col-xs-5">Host Status</th>
<th class="col-lg-1 col-md-1 hidden-sm hidden-xs">Elapsed</th>
</tr>
</thead>
<tbody id="task-table-body">
<tr ng-repeat="task in tasks | filter:{ play_id: activePlay }" ng-class="task.taskActiveClass" ng-click="selectTask(task.id)" class="cursor-pointer">
<td class="col-lg-1 col-md-1 col-sm-2 hidden-xs">{{ task.created | date: 'HH:mm:ss' }}</td>
<td class="col-lg-6 col-md-6 col-sm-6 col-xs-7 status-column">
<div class="table-header">
<div class="row">
<div class="col-lg-1 col-md-1 col-sm-2 hidden-xs">Started</div>
<div class="col-lg-1 col-md-1 hidden-sm hidden-xs">Elapsed</div>
<div class="col-lg-5 col-md-5 col-sm-10 col-xs-12">Name</div>
<div class="col-lg-5 col-md-5 hidden-xs hidden-sm">Host Status</div>
</div>
</div>
<div id="tasks-table-detail" class="table-detail">
<div class="row cursor-pointer" ng-repeat="task in tasks | filter:{ play_id: activePlay }" ng-class="task.taskActiveClass"
ng-click="selectTask(task.id)">
<div class="col-lg-1 col-md-1 col-sm-2 hidden-xs">{{ task.created | date: 'HH:mm:ss' }}</div>
<div class="col-lg-1 col-md-1 hidden-sm hidden-xs" aw-tool-tip="Completed at {{ task.finished | date:'HH:mm:ss' }}"
data-placement="top">{{ task.elapsed }}
</div>
<div class="col-lg-5 col-md-5 col-sm-10 col-xs-12 status-column">
<i class="fa icon-job-{{ task.status }}"></i><span ng-show="hasRoles"> {{ task.role }} </span>{{ task.name }}
</td>
<td class="col-lg-4 col-md-4 col-sm-4 col-xs-5">
<div class="status-bar">
<div class="successful-hosts inner-bar" aw-tool-tip="{{ task.successfulCount}} hosts OK" aw-tip-watch="task.successfulCount" data-placement="top" ng-style="{{ task.successfulStyle }}">{{ task.successfulCount }}</div>
<div class="changed-hosts inner-bar" aw-tool-tip="{{ task.changedCount}} hosts changed" aw-tip-watch="task.changedCount" data-placement="top" ng-style="{{ task.changedStyle }}">{{ task.changedCount }}</div>
<div class="skipped-hosts inner-bar" aw-tool-tip="{{ task.skippedCount}} hosts skipped" aw-tip-watch="task.skippedCount" data-placement="top" ng-style="{{ task.skippedStyle }}">{{ task.skippedCount }}</div>
<div class="failed-hosts inner-bar" aw-tool-tip="{{ task.failedCount}} hosts failed" aw-tip-watch="task.failedCount" data-placement="top" ng-style="{{ task.failedStyle }}">{{ task.failedCount }}</div>
</div>
</td>
<td class="col-lg-1 col-md-1 hidden-sm hidden-xs" aw-tool-tip="Completed at {{ task.finished | date:'HH:mm:ss' }}"
data-placement="top">{{ task.elapsed }}</td>
</tr>
</tbody>
</table>
</div>
<div class="col-lg-5 col-md-5 hidden-sm hidden-xs">
<div class="status-bar">
<div class="successful-hosts inner-bar" aw-tool-tip="{{ task.successfulCount}} hosts OK" aw-tip-watch="task.successfulCount" data-placement="top" ng-style="{{ task.successfulStyle }}">{{ task.successfulCount }}</div>
<div class="changed-hosts inner-bar" aw-tool-tip="{{ task.changedCount}} hosts changed" aw-tip-watch="task.changedCount" data-placement="top" ng-style="{{ task.changedStyle }}">{{ task.changedCount }}</div>
<div class="skipped-hosts inner-bar" aw-tool-tip="{{ task.skippedCount}} hosts skipped" aw-tip-watch="task.skippedCount" data-placement="top" ng-style="{{ task.skippedStyle }}">{{ task.skippedCount }}</div>
<div class="failed-hosts inner-bar" aw-tool-tip="{{ task.failedCount}} hosts failed" aw-tip-watch="task.failedCount" data-placement="top" ng-style="{{ task.failedStyle }}">{{ task.failedCount }}</div>
</div>
</div>
</div>
</div>
</div><!-- section -->
<div class="section" style="position: relative;">
<div id="task-hosts-section" class="section">
<h5>Hosts</h5>
<div id="hosts-section">
<div id="host-details">
<ul>
<li ng-repeat="result in hostResults | filter:{ task_id: activeTask }">
<div class="status-column inline-block"><i class="fa icon-job-{{ result.status }}"></i> <a href="" ng-click="doSomething()" aw-tool-tip="Click to view results" data-placement="top">{{ result.name }}</a></div>
</li>
</ul>
<div id="hosts-table-detail" class="table-detail">
<div class="row" ng-repeat="result in hostResults | filter:{ task_id: activeTask }">
<div class="col-lg-1 col-md-1 col-sm-1 col-xs-1 status-column">
<a href="" ng-click="doSomething()" aw-tool-tip="Status: {{ result.status }}. Click for details" data-placement="top">
<i class="fa icon-job-{{ result.status }}"></i></a>
</div>
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
<a href="" ng-click="doSomething()" aw-tool-tip="Status: {{ result.status }}. Click for details" data-placement="top">{{ result.name }}</a>
</div>
<div class="col-lg-5 col-md-5 col-sm-5 col-xs-5">
{{ result.msg }}
</div>
</div>
</div>
<div id="hosts-slider-vertical" style="position:absolute; top:27px; right: 0; width:9px; height:150px;"></div>
@ -105,30 +118,29 @@
</div><!-- col-md-7 -->
<div class="col-md-5">
<div id="job-summary-container">
<div class="job_well">
<div class="section job_summary">
<div id="hosts-summary-section" class="section job_summary">
<h5>Host Summary</h5>
<table class="table table-condensed">
<thead>
<tr>
<th class="col-lg-8 col-md-8 col-sm-8 col-xs-4">Host</th>
<th class="col-lg-1 col-md-1 col-sm-1 col-xs-2">OK</th>
<th class="col-lg-1 col-md-1 col-sm-1 col-xs-2">Changed</th>
<th class="col-lg-1 col-md-1 col-sm-1 col-xs-2">Dark</th>
<th class="col-lg-1 col-md-1 col-sm-1 col-xs-2">Failed</th>
</tr>
</thead>
<tbody >
<tr ng-repeat="host in hosts">
<td class="col-lg-8 col-md-8 col-sm-8 col-xs-4"><a href="/#/home/hosts/?id={{ host.id }}">{{ host.name }}</a></td>
<td class="col-lg-1 col-md-1 col-sm-1 col-xs-2">{{ host.ok }}</td>
<td class="col-lg-1 col-md-1 col-sm-1 col-xs-2">{{ host.changed }}</td>
<td class="col-lg-1 col-md-1 col-sm-1 col-xs-2">{{ host.unreachable }}</td>
<td class="col-lg-1 col-md-1 col-sm-1 col-xs-2">{{ host.failed }}</td>
</tr>
</tbody>
</table>
<div class="table-header">
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-4 col-xs-4">Host</div>
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">OK</div>
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">Change</div>
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">Dark</div>
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">Fail</div>
</div>
</div>
<div class="table-detail">
<div class="row" ng-repeat="host in hosts">
<div class="name col-lg-4 col-md-4 col-sm-4 col-xs-4"><a href="/#/home/hosts/?id={{ host.id }}"
aw-tool-tip="View host" data-placement="top">{{ host.name }}</a></div>
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">{{ host.ok }}</div>
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">{{ host.changed }}</div>
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">{{ host.unreachable }}</div>
<div class="col-lg-2 col-md-2 col-sm-2 col-xs-2">{{ host.failed }}</div>
</div>
</div>
</div><!-- section -->
</div>
</div><!-- col-md-5 -->