mirror of
https://github.com/ansible/awx.git
synced 2026-03-07 11:41:08 -03:30
Merge pull request #253 from joefiorini/system-tracking-improvements
System tracking improvements
This commit is contained in:
@@ -11,10 +11,11 @@ export default
|
|||||||
|
|
||||||
$scope.breadcrumbs = [];
|
$scope.breadcrumbs = [];
|
||||||
|
|
||||||
this.addBreadcrumb = function(title, path) {
|
this.addBreadcrumb = function(title, path, isCurrent) {
|
||||||
var breadcrumb =
|
var breadcrumb =
|
||||||
{ title: title,
|
{ title: title,
|
||||||
path: path
|
path: path,
|
||||||
|
isCurrent: isCurrent
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($rootScope.enteredPath === path) {
|
if ($rootScope.enteredPath === path) {
|
||||||
|
|||||||
@@ -35,7 +35,13 @@ function lodashAsPromised($q) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _then(promise, fn) {
|
function _then(promise, fn) {
|
||||||
return promise.then(fn);
|
return promise.then(function(value) {
|
||||||
|
if (_.isFunction(value.then)) {
|
||||||
|
return _then(value, fn);
|
||||||
|
} else {
|
||||||
|
return fn(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _catch(promise, fn) {
|
function _catch(promise, fn) {
|
||||||
|
|||||||
@@ -4,10 +4,16 @@
|
|||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
|
import {wrapDelegate} from './route-params.decorator';
|
||||||
|
|
||||||
export default
|
export default
|
||||||
[ '$rootScope',
|
[ '$rootScope',
|
||||||
'$routeParams',
|
'$routeParams',
|
||||||
function($rootScope, $routeParams) {
|
function($rootScope, $routeParams) {
|
||||||
|
$rootScope.$on('$routeChangeStart', function(e, newRoute) {
|
||||||
|
wrapDelegate(newRoute.params);
|
||||||
|
});
|
||||||
|
|
||||||
$rootScope.$on('$routeChangeSuccess', function(e, newRoute) {
|
$rootScope.$on('$routeChangeSuccess', function(e, newRoute) {
|
||||||
if (angular.isUndefined(newRoute.model)) {
|
if (angular.isUndefined(newRoute.model)) {
|
||||||
var keys = Object.keys(newRoute.params);
|
var keys = Object.keys(newRoute.params);
|
||||||
|
|||||||
@@ -7,23 +7,23 @@
|
|||||||
export default
|
export default
|
||||||
function flatCompare(facts, nameKey, compareKeys) {
|
function flatCompare(facts, nameKey, compareKeys) {
|
||||||
|
|
||||||
var leftFacts = facts[0];
|
var comparatorFacts = facts[0];
|
||||||
var rightFacts = facts[1];
|
var basisFacts = facts[1];
|
||||||
|
|
||||||
return rightFacts.reduce(function(arr, rightFact) {
|
return basisFacts.reduce(function(arr, basisFact) {
|
||||||
var searcher = {};
|
var searcher = {};
|
||||||
searcher[nameKey] = rightFact[nameKey];
|
searcher[nameKey] = basisFact[nameKey];
|
||||||
|
|
||||||
var isNewFactValue = false;
|
var isNewFactValue = false;
|
||||||
|
|
||||||
var matchingFact = _.where(leftFacts, searcher);
|
var matchingFact = _.where(comparatorFacts, searcher);
|
||||||
var diffs;
|
var diffs;
|
||||||
|
|
||||||
if (_.isEmpty(matchingFact)) {
|
if (_.isEmpty(matchingFact)) {
|
||||||
isNewFactValue = true;
|
isNewFactValue = true;
|
||||||
|
|
||||||
diffs =
|
diffs =
|
||||||
_.map(rightFact, function(value, key) {
|
_.map(basisFact, function(value, key) {
|
||||||
return { keyName: key,
|
return { keyName: key,
|
||||||
value1: value,
|
value1: value,
|
||||||
value2: ''
|
value2: ''
|
||||||
@@ -34,8 +34,18 @@ export default
|
|||||||
|
|
||||||
diffs = _(compareKeys)
|
diffs = _(compareKeys)
|
||||||
.map(function(key) {
|
.map(function(key) {
|
||||||
var leftValue = rightFact[key];
|
var basisValue = basisFact[key];
|
||||||
var rightValue = matchingFact[key];
|
var comparatorValue = matchingFact[key];
|
||||||
|
var leftValue, rightValue;
|
||||||
|
|
||||||
|
if (basisFacts.position === 'left') {
|
||||||
|
leftValue = basisValue;
|
||||||
|
rightValue = comparatorValue;
|
||||||
|
} else {
|
||||||
|
rightValue = basisValue;
|
||||||
|
leftValue = comparatorValue;
|
||||||
|
}
|
||||||
|
|
||||||
if (leftValue !== rightValue) {
|
if (leftValue !== rightValue) {
|
||||||
return {
|
return {
|
||||||
keyName: key,
|
keyName: key,
|
||||||
@@ -49,7 +59,7 @@ export default
|
|||||||
}
|
}
|
||||||
|
|
||||||
var descriptor =
|
var descriptor =
|
||||||
{ displayKeyPath: rightFact[nameKey],
|
{ displayKeyPath: basisFact[nameKey],
|
||||||
isNew: isNewFactValue,
|
isNew: isNewFactValue,
|
||||||
nestingLevel: 0,
|
nestingLevel: 0,
|
||||||
facts: diffs
|
facts: diffs
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ function (Rest, GetBasePath, ProcessErrors, _) {
|
|||||||
return version
|
return version
|
||||||
.then(function(versionData) {
|
.then(function(versionData) {
|
||||||
if (_.isEmpty(versionData)) {
|
if (_.isEmpty(versionData)) {
|
||||||
return [];
|
return { fact: [] };
|
||||||
} else {
|
} else {
|
||||||
return getFacts(versionData);
|
return getFacts(versionData);
|
||||||
}
|
}
|
||||||
@@ -26,8 +26,8 @@ function (Rest, GetBasePath, ProcessErrors, _) {
|
|||||||
var promise;
|
var promise;
|
||||||
Rest.setUrl(version.related.fact_view);
|
Rest.setUrl(version.related.fact_view);
|
||||||
promise = Rest.get();
|
promise = Rest.get();
|
||||||
return promise.then(function (data) {
|
return promise.then(function (response) {
|
||||||
return data.data.fact;
|
return response.data;
|
||||||
}).catch(function (response) {
|
}).catch(function (response) {
|
||||||
ProcessErrors(null, response.data, response.status, null, {
|
ProcessErrors(null, response.data, response.status, null, {
|
||||||
hdr: 'Error!',
|
hdr: 'Error!',
|
||||||
|
|||||||
@@ -6,17 +6,24 @@
|
|||||||
|
|
||||||
export default
|
export default
|
||||||
[ 'factScanDataService',
|
[ 'factScanDataService',
|
||||||
|
'getModuleOptions',
|
||||||
'lodashAsPromised',
|
'lodashAsPromised',
|
||||||
function(factScanDataService, _) {
|
function(factScanDataService, getModuleOptions, _) {
|
||||||
return function(hostIds, moduleName, leftDate, rightDate) {
|
return function(hostIds, moduleName, leftDate, rightDate) {
|
||||||
|
|
||||||
|
var moduleOptions;
|
||||||
|
|
||||||
if (hostIds.length === 1) {
|
if (hostIds.length === 1) {
|
||||||
hostIds = hostIds.concat(hostIds[0]);
|
hostIds = hostIds.concat(hostIds[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(hostIds)
|
return getModuleOptions(hostIds[0])
|
||||||
.promise()
|
.then(function(modules) {
|
||||||
.thenMap(function(hostId, index) {
|
moduleOptions = modules;
|
||||||
|
return modules;
|
||||||
|
}).then(function() {
|
||||||
|
return hostIds;
|
||||||
|
}).thenMap(function(hostId, index) {
|
||||||
var date = leftDate;
|
var date = leftDate;
|
||||||
var fetchScanNumber;
|
var fetchScanNumber;
|
||||||
|
|
||||||
@@ -42,6 +49,8 @@ export default
|
|||||||
.bind(factScanDataService);
|
.bind(factScanDataService);
|
||||||
|
|
||||||
return getHostFacts(params);
|
return getHostFacts(params);
|
||||||
|
}).then(function(hostFacts) {
|
||||||
|
return [moduleOptions, hostFacts];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
var moduleConfig =
|
||||||
|
{ 'packages':
|
||||||
|
{ compareKey: ['release', 'version'],
|
||||||
|
nameKey: 'name',
|
||||||
|
displayType: 'flat',
|
||||||
|
sortKey: 1
|
||||||
|
},
|
||||||
|
'services':
|
||||||
|
{ compareKey: ['state', 'source'],
|
||||||
|
nameKey: 'name',
|
||||||
|
displayType: 'flat',
|
||||||
|
sortKey: 2
|
||||||
|
},
|
||||||
|
'files':
|
||||||
|
{ compareKey: ['size', 'mode', 'md5', 'mtime', 'gid', 'uid'],
|
||||||
|
nameKey: 'path',
|
||||||
|
displayType: 'flat',
|
||||||
|
sortKey: 3
|
||||||
|
},
|
||||||
|
'ansible':
|
||||||
|
{ displayType: 'nested',
|
||||||
|
sortKey: 4
|
||||||
|
},
|
||||||
|
'custom':
|
||||||
|
{ displayType: 'nested'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function makeModule(option, index) {
|
||||||
|
var name = option[0];
|
||||||
|
var displayName = option[1];
|
||||||
|
var config = moduleConfig.hasOwnProperty(name) ?
|
||||||
|
moduleConfig[name] : moduleConfig.custom;
|
||||||
|
var modulesCount = _.keys(moduleConfig).length - 1;
|
||||||
|
|
||||||
|
config.name = name;
|
||||||
|
config.displayName = displayName;
|
||||||
|
|
||||||
|
// Use index to sort custom modules,
|
||||||
|
// offset by built-in modules since
|
||||||
|
// they have a hardcoded sort key
|
||||||
|
//
|
||||||
|
if (_.isUndefined(config.sortKey)) {
|
||||||
|
config.sortKey = (index - 1) + modulesCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
function factory(hostId, rest, getBasePath, _) {
|
||||||
|
var url = [ getBasePath('hosts') + hostId,
|
||||||
|
'fact_versions'
|
||||||
|
].join('/');
|
||||||
|
|
||||||
|
rest.setUrl(url);
|
||||||
|
return _(rest.options())
|
||||||
|
.then(function(response) {
|
||||||
|
var choices = response.data.actions.GET.module.choices;
|
||||||
|
return _.sortBy(choices, '1');
|
||||||
|
}).thenMap(makeModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default
|
||||||
|
[ 'Rest',
|
||||||
|
'GetBasePath',
|
||||||
|
'lodashAsPromised',
|
||||||
|
function(rest, getBasePath, lodash) {
|
||||||
|
return _.partialRight(factory, rest, getBasePath, lodash);
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -3,9 +3,13 @@
|
|||||||
.FactModuleFilter {
|
.FactModuleFilter {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
margin-bottom: 2.8rem;
|
margin-bottom: 2.8rem;
|
||||||
&-module {
|
&-module {
|
||||||
flex: 1;
|
flex: 1 1 20%;
|
||||||
|
@media screen and (max-width: 750px) {
|
||||||
|
flex-basis: 50%;
|
||||||
|
}
|
||||||
&--isActive {
|
&--isActive {
|
||||||
// copied from bootstrap's .btn:focus
|
// copied from bootstrap's .btn:focus
|
||||||
background-color: #ebebeb;
|
background-color: #ebebeb;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import route from './system-tracking.route';
|
import route from './system-tracking.route';
|
||||||
import factScanDataService from './data-services/fact-scan-data.service';
|
import factScanDataService from './data-services/fact-scan-data.service';
|
||||||
import getDataForComparison from './data-services/get-data-for-comparison.factory';
|
import getDataForComparison from './data-services/get-data-for-comparison.factory';
|
||||||
|
import getModuleOptions from './data-services/get-module-options.factory';
|
||||||
import controller from './system-tracking.controller';
|
import controller from './system-tracking.controller';
|
||||||
import stringOrDateFilter from './string-or-date.filter';
|
import stringOrDateFilter from './string-or-date.filter';
|
||||||
import shared from 'tower/shared/main';
|
import shared from 'tower/shared/main';
|
||||||
@@ -22,6 +23,7 @@ export default
|
|||||||
])
|
])
|
||||||
.service('factScanDataService', factScanDataService)
|
.service('factScanDataService', factScanDataService)
|
||||||
.factory('getDataForComparison', getDataForComparison)
|
.factory('getDataForComparison', getDataForComparison)
|
||||||
|
.factory('getModuleOptions', getModuleOptions)
|
||||||
.filter('stringOrDate', stringOrDateFilter)
|
.filter('stringOrDate', stringOrDateFilter)
|
||||||
.controller('systemTracking', controller)
|
.controller('systemTracking', controller)
|
||||||
.config(['$routeProvider', function($routeProvider) {
|
.config(['$routeProvider', function($routeProvider) {
|
||||||
|
|||||||
@@ -4,9 +4,23 @@
|
|||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
export function searchDateRange(date) {
|
export function searchDateRange(dateString) {
|
||||||
|
var date;
|
||||||
|
|
||||||
|
switch(dateString) {
|
||||||
|
case 'yesterday':
|
||||||
|
date = moment().subtract(1, 'day');
|
||||||
|
break;
|
||||||
|
case 'tomorrow':
|
||||||
|
date = moment().add(1, 'day');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
date = moment(dateString);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
from: moment(date).startOf('day'),
|
from: date.clone().startOf('day'),
|
||||||
to: moment(date).endOf('day')
|
to: date.clone().endOf('day')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,90 +19,124 @@ function controller($rootScope,
|
|||||||
_) {
|
_) {
|
||||||
// var inventoryId = $routeParams.id;
|
// var inventoryId = $routeParams.id;
|
||||||
var hostIds = $routeParams.hosts.split(',');
|
var hostIds = $routeParams.hosts.split(',');
|
||||||
|
var hosts = $routeParams.model.hosts;
|
||||||
|
|
||||||
|
$scope.hostIds = $routeParams.hosts;
|
||||||
|
$scope.inventory = $routeParams.model.inventory;
|
||||||
|
|
||||||
$scope.factModulePickersLabelLeft = "Compare facts collected on";
|
$scope.factModulePickersLabelLeft = "Compare facts collected on";
|
||||||
$scope.factModulePickersLabelRight = "To facts collected on";
|
$scope.factModulePickersLabelRight = "To facts collected on";
|
||||||
|
|
||||||
$scope.modules =
|
$scope.modules = initialFactData.moduleOptions;
|
||||||
[{ name: 'packages',
|
|
||||||
displayName: 'Packages',
|
|
||||||
compareKey: ['release', 'version'],
|
|
||||||
nameKey: 'name',
|
|
||||||
isActive: true,
|
|
||||||
displayType: 'flat'
|
|
||||||
},
|
|
||||||
{ name: 'services',
|
|
||||||
compareKey: ['state', 'source'],
|
|
||||||
nameKey: 'name',
|
|
||||||
displayName: 'Services',
|
|
||||||
isActive: false,
|
|
||||||
displayType: 'flat'
|
|
||||||
},
|
|
||||||
{ name: 'files',
|
|
||||||
displayName: 'Files',
|
|
||||||
nameKey: 'path',
|
|
||||||
compareKey: ['size', 'mode', 'md5', 'mtime', 'gid', 'uid'],
|
|
||||||
isActive: false,
|
|
||||||
displayType: 'flat'
|
|
||||||
},
|
|
||||||
{ name: 'ansible',
|
|
||||||
displayName: 'Ansible',
|
|
||||||
isActive: false,
|
|
||||||
displayType: 'nested'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// Use this to determine how to orchestrate the services
|
// Use this to determine how to orchestrate the services
|
||||||
var viewType = hostIds.length > 1 ? 'multiHost' : 'singleHost';
|
var viewType = hostIds.length > 1 ? 'multiHost' : 'singleHost';
|
||||||
|
|
||||||
var searchConfig =
|
var searchConfig =
|
||||||
{ leftDate: initialFactData.leftDate,
|
{ leftRange: initialFactData.leftSearchRange,
|
||||||
rightDate: initialFactData.rightDate
|
rightRange: initialFactData.rightSearchRange
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.leftDate = initialFactData.leftDate.from;
|
$scope.leftDate = initialFactData.leftSearchRange.from;
|
||||||
$scope.rightDate = initialFactData.rightDate.from;
|
$scope.rightDate = initialFactData.rightSearchRange.from;
|
||||||
|
|
||||||
function setHeaderValues(viewType) {
|
function setHeaderValues(viewType) {
|
||||||
if (viewType === 'singleHost') {
|
if (viewType === 'singleHost') {
|
||||||
$scope.comparisonLeftHeader = $scope.leftDate;
|
$scope.comparisonLeftHeader = $scope.leftScanDate;
|
||||||
$scope.comparisonRightHeader = $scope.rightDate;
|
$scope.comparisonRightHeader = $scope.rightScanDate;
|
||||||
} else {
|
} else {
|
||||||
$scope.comparisonLeftHeader = hostIds[0];
|
$scope.comparisonLeftHeader = hosts[0].name;
|
||||||
$scope.comparisonRightHeader = hostIds[1];
|
$scope.comparisonRightHeader = hosts[1].name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadData(params, initialData) {
|
function reloadData(params, initialData) {
|
||||||
|
|
||||||
searchConfig = _.merge({}, searchConfig, params);
|
searchConfig = _.merge({}, searchConfig, params);
|
||||||
|
|
||||||
var factData = initialData;
|
var factData = initialData;
|
||||||
var leftDate = searchConfig.leftDate;
|
var leftRange = searchConfig.leftRange;
|
||||||
var rightDate = searchConfig.rightDate;
|
var rightRange = searchConfig.rightRange;
|
||||||
var activeModule = searchConfig.module;
|
var activeModule = searchConfig.module;
|
||||||
|
var leftScanDate, rightScanDate;
|
||||||
|
|
||||||
|
|
||||||
if (!factData) {
|
if (!factData) {
|
||||||
factData = getDataForComparison(
|
factData =
|
||||||
hostIds,
|
getDataForComparison(
|
||||||
activeModule.name,
|
hostIds,
|
||||||
leftDate,
|
activeModule.name,
|
||||||
rightDate);
|
leftRange,
|
||||||
|
rightRange)
|
||||||
|
.thenAll(function(factDataAndModules) {
|
||||||
|
var responses = factDataAndModules[1];
|
||||||
|
var data = _.pluck(responses, 'fact');
|
||||||
|
|
||||||
|
leftScanDate = moment(responses[0].timestamp);
|
||||||
|
rightScanDate = moment(responses[1].timestamp);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
waitIndicator('start');
|
waitIndicator('start');
|
||||||
|
|
||||||
_(factData)
|
return _(factData)
|
||||||
.thenAll(_.partial(compareFacts, activeModule))
|
.thenAll(function(facts) {
|
||||||
|
// Make sure we always start comparison against
|
||||||
|
// a non-empty array
|
||||||
|
//
|
||||||
|
// Partition with _.isEmpty will give me an array
|
||||||
|
// with empty arrays in index 0, and non-empty
|
||||||
|
// arrays in index 1
|
||||||
|
//
|
||||||
|
|
||||||
|
// Save the position of the data so we
|
||||||
|
// don't lose it later
|
||||||
|
|
||||||
|
facts[0].position = 'left';
|
||||||
|
facts[1].position = 'right';
|
||||||
|
|
||||||
|
var splitFacts = _.partition(facts, _.isEmpty);
|
||||||
|
var emptyScans = splitFacts[0];
|
||||||
|
var nonEmptyScans = splitFacts[1];
|
||||||
|
|
||||||
|
if (_.isEmpty(nonEmptyScans)) {
|
||||||
|
// we have NO data, throw an error
|
||||||
|
throw {
|
||||||
|
name: 'NoScanData',
|
||||||
|
message: 'No scans ran on eithr of the dates you selected. Please try selecting different dates.',
|
||||||
|
dateValues:
|
||||||
|
{ leftDate: $scope.leftDate.clone(),
|
||||||
|
rightDate: $scope.rightDate.clone()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (nonEmptyScans.length === 1) {
|
||||||
|
// one of them is not empty, throw an error
|
||||||
|
throw {
|
||||||
|
name: 'InsufficientScanData',
|
||||||
|
message: 'No scans ran on one of the selected dates. Please try selecting a different date.',
|
||||||
|
dateValue: emptyScans[0].position === 'left' ? $scope.leftDate.clone() : $scope.rightDate.clone()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// all scans have data, rejoice!
|
||||||
|
return facts;
|
||||||
|
|
||||||
|
})
|
||||||
|
.then(_.partial(compareFacts, activeModule))
|
||||||
.then(function(info) {
|
.then(function(info) {
|
||||||
|
|
||||||
|
// Clear out any errors from the previous run...
|
||||||
|
$scope.error = null;
|
||||||
|
|
||||||
$scope.factData = info;
|
$scope.factData = info;
|
||||||
|
|
||||||
setHeaderValues(viewType);
|
setHeaderValues(viewType, leftScanDate, rightScanDate);
|
||||||
|
|
||||||
}).finally(function() {
|
}).finally(function() {
|
||||||
waitIndicator('stop');
|
waitIndicator('stop');
|
||||||
})
|
});
|
||||||
.value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.setActiveModule = function(newModuleName, initialData) {
|
$scope.setActiveModule = function(newModuleName, initialData) {
|
||||||
@@ -120,9 +154,12 @@ function controller($rootScope,
|
|||||||
$location.replace();
|
$location.replace();
|
||||||
$location.search('module', newModuleName);
|
$location.search('module', newModuleName);
|
||||||
|
|
||||||
reloadData(
|
reloadData({ module: newModule
|
||||||
{ module: newModule
|
}, initialData)
|
||||||
}, initialData);
|
|
||||||
|
.catch(function(error) {
|
||||||
|
$scope.error = error;
|
||||||
|
}).value();
|
||||||
};
|
};
|
||||||
|
|
||||||
function dateWatcher(dateProperty) {
|
function dateWatcher(dateProperty) {
|
||||||
@@ -141,13 +178,16 @@ function controller($rootScope,
|
|||||||
var params = {};
|
var params = {};
|
||||||
params[dateProperty] = newDate;
|
params[dateProperty] = newDate;
|
||||||
|
|
||||||
reloadData(params);
|
reloadData(params)
|
||||||
|
.catch(function(error) {
|
||||||
|
$scope.error = error;
|
||||||
|
}).value();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$watch('leftDate', dateWatcher('leftDate'), true);
|
$scope.$watch('leftDate', dateWatcher('leftRange'), true);
|
||||||
|
|
||||||
$scope.$watch('rightDate', dateWatcher('rightDate'), true);
|
$scope.$watch('rightDate', dateWatcher('rightRange'), true);
|
||||||
|
|
||||||
$scope.setActiveModule(initialFactData.moduleName, initialFactData);
|
$scope.setActiveModule(initialFactData.moduleName, initialFactData);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,92 @@
|
|||||||
<div class="FactModulePickers">
|
<section class="SystemTrackingContainer">
|
||||||
<div class="FactModulePickers-dateContainer FactModulePickers-dateContainer--left">
|
<breadcrumbs>
|
||||||
<span class="FactModulePickers-label">{{ factModulePickersLabelLeft }}</span>
|
<breadcrumb path="/inventories" title="Inventories"></breadcrumb>
|
||||||
<date-picker date="leftDate"></date-picker>
|
<breadcrumb path="/inventories/{{inventory.id}}/manage" title="{{inventory.name}}"></breadcrumb>
|
||||||
</div>
|
<breadcrumb path="/inventories/{{inventory.id}}/system-tracking/{{hostIds}}" title="System Tracking" current="true"></breadcrumb>
|
||||||
<div class="FactModulePickers-dateContainer FactModulePickers-dateContainer--right">
|
</breadcrumbs>
|
||||||
<span class="FactModulePickers-label">{{ factModulePickersLabelRight }}</span>
|
|
||||||
<date-picker date="rightDate"></date-picker>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<nav class="btn-group FactModuleFilter">
|
<div class="FactModulePickers">
|
||||||
<button
|
<div class="FactModulePickers-dateContainer FactModulePickers-dateContainer--left">
|
||||||
ng-class="{ 'btn': true,
|
<span class="FactModulePickers-label">{{ factModulePickersLabelLeft }}</span>
|
||||||
'btn-default': true,
|
<date-picker date="leftDate"></date-picker>
|
||||||
'Button--pseudo': true,
|
|
||||||
'FactModuleFilter-module': true,
|
|
||||||
'FactModuleFilter-module--isActive': module.isActive,
|
|
||||||
}"
|
|
||||||
ng-click="setActiveModule(module.name)"
|
|
||||||
ng-repeat="module in modules">
|
|
||||||
{{module.displayName}}
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<section class="FactDataError" ng-if="error" ng-switch="error">
|
|
||||||
<p class="FactDataError-message" ng-switch-when="NoFactsForModule">
|
|
||||||
There were no facts collected for that module in the selected date range. Please pick a different range or module and try again.
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section class="FactDataTable" ng-unless="error">
|
|
||||||
<div class="FactDataTable-row">
|
|
||||||
<h3 class="FactDataTable-column FactDataTable-column--offsetLeft">{{comparisonLeftHeader|stringOrDate:'L'}}</h3>
|
|
||||||
<h3 class="FactDataTable-column">{{comparisonRightHeader|stringOrDate:'L'}}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="FactDataTable-factGroup FactDataGroup" ng-repeat="group in factData | orderBy: 'displayKeyPath'">
|
|
||||||
<div class="FactDataTable-row FactDataGroup-headings" ng-switch="group.nestingLevel" ng-if="group.displayKeyPath">
|
|
||||||
<h2 class="FactDataTable-column FactDataTable-column--full FactDataGroup-header" ng-class="{ 'FactDataGroup-header--new': group.isNew }" ng-switch-when="0">
|
|
||||||
{{group.displayKeyPath}}
|
|
||||||
</h2>
|
|
||||||
<h3 class="FactDataTable-column FactDataTable-column--full" ng-switch-when="1">
|
|
||||||
{{group.displayKeyPath}}
|
|
||||||
</h3>
|
|
||||||
<h4 class="FactDataTable-column FactDataTable-column--full" ng-switch-when="2">
|
|
||||||
{{group.displayKeyPath}}
|
|
||||||
</h4>
|
|
||||||
<h5 class="FactDataTable-column FactDataTable-column--full" ng-switch-when="3">
|
|
||||||
{{group.displayKeyPath}}
|
|
||||||
</h5>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="FactDataGroup-facts" data-facts="{{group.facts}}">
|
<div class="FactModulePickers-dateContainer FactModulePickers-dateContainer--right">
|
||||||
<div class="FactDataTable-arrayGroup" ng-if="group.isFactArray" ng-repeat="arrayGroup in group.facts" data-array-group="{{arrayGroup}}">
|
<span class="FactModulePickers-label">{{ factModulePickersLabelRight }}</span>
|
||||||
<div class="FactDataTable-row FactDatum" ng-class="{'FactDatum--divergent': fact.isDivergent }" ng-repeat="fact in arrayGroup" data-fact="{{fact}}">
|
<date-picker date="rightDate"></date-picker>
|
||||||
<p class="FactDatum-keyName FactDataTable-column">
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="btn-group FactModuleFilter">
|
||||||
|
<button
|
||||||
|
ng-class="{ 'btn': true,
|
||||||
|
'btn-default': true,
|
||||||
|
'FactModuleFilter-module': true,
|
||||||
|
'FactModuleFilter-module--isActive': module.isActive,
|
||||||
|
}"
|
||||||
|
ng-click="setActiveModule(module.name)"
|
||||||
|
ng-repeat="module in modules | orderBy: 'sortKey'">
|
||||||
|
{{module.displayName}}
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section class="FactDataError SystemTrackingContainer-main" ng-if="error" ng-switch="error.name">
|
||||||
|
<p class="FactDataError-message" ng-switch-when="NoScanData">
|
||||||
|
There were no facts collected on the dates you selected ({{error.dateValues.leftDate|amDateFormat:'L'}} and {{error.dateValues.rightDate|amDateFormat:'L'}}). Please pick a different range or module and try again.
|
||||||
|
</p>
|
||||||
|
<p class="FactDataError-message" ng-switch-when="InsufficientScanData">
|
||||||
|
There were no facts collected on one of the dates you selected ({{error.dateValue|amDateFormat:'L'}}). Please select a different date and try again.
|
||||||
|
</p>
|
||||||
|
<p class="FactDataError-note">
|
||||||
|
To setup or run scan jobs, edit the "<a link-to="inventoryEdit" model="{ inventory_id: inventory }">{{inventory.name}}</a>" inventory and select "Scan Jobs Templates".
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section class="FactDataTable SystemTrackingContainer-main" ng-if="!error">
|
||||||
|
<div class="FactDataTable-row">
|
||||||
|
<h3 class="FactDataTable-column FactDataTable-column--offsetLeft">{{comparisonLeftHeader|stringOrDate:'L LT'}}</h3>
|
||||||
|
<h3 class="FactDataTable-column">{{comparisonRightHeader|stringOrDate:'L LT'}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="FactDataTable-factGroup FactDataGroup" ng-repeat="group in factData | orderBy: 'displayKeyPath'">
|
||||||
|
<div class="FactDataTable-row FactDataGroup-headings" ng-switch="group.nestingLevel" ng-if="group.displayKeyPath">
|
||||||
|
<h2 class="FactDataTable-column FactDataTable-column--full FactDataGroup-header" ng-class="{ 'FactDataGroup-header--new': group.isNew }" ng-switch-when="0">
|
||||||
|
{{group.displayKeyPath}}
|
||||||
|
</h2>
|
||||||
|
<h3 class="FactDataTable-column FactDataTable-column--full" ng-switch-when="1">
|
||||||
|
{{group.displayKeyPath}}
|
||||||
|
</h3>
|
||||||
|
<h4 class="FactDataTable-column FactDataTable-column--full" ng-switch-when="2">
|
||||||
|
{{group.displayKeyPath}}
|
||||||
|
</h4>
|
||||||
|
<h5 class="FactDataTable-column FactDataTable-column--full" ng-switch-when="3">
|
||||||
|
{{group.displayKeyPath}}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div class="FactDataGroup-facts" data-facts="{{group.facts}}">
|
||||||
|
<div class="FactDataTable-arrayGroup" ng-if="group.isFactArray" ng-repeat="arrayGroup in group.facts" data-array-group="{{arrayGroup}}">
|
||||||
|
<div class="FactDataTable-row FactDatum" ng-class="{'FactDatum--divergent': fact.isDivergent }" ng-repeat="fact in arrayGroup" data-fact="{{fact}}">
|
||||||
|
<p class="FactDatum-keyName FactDataTable-column">
|
||||||
|
{{fact.keyName}}
|
||||||
|
</p>
|
||||||
|
<p class="FactDatum-value FactDataTable-column" style="word-break: break-all">
|
||||||
|
{{fact.value1}}
|
||||||
|
</p>
|
||||||
|
<p class="FactDatum-value FactDataTable-column" style="word-break: break-all">
|
||||||
|
{{fact.value2}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="FactDataTable-row FactDatum" ng-class="{'FactDatum--divergent': fact.isDivergent }" ng-repeat="fact in group.facts" ng-unless="group.isFactArray" data-fact="{{fact}}">
|
||||||
|
<p class="FactDataTable-column FactDatum-keyName">
|
||||||
{{fact.keyName}}
|
{{fact.keyName}}
|
||||||
</p>
|
</p>
|
||||||
<p class="FactDatum-value FactDataTable-column" style="word-break: break-all">
|
<p class="FactDataTable-column FactDatum-value" style="word-break: break-all">
|
||||||
{{fact.value1}}
|
{{fact.value1}}
|
||||||
</p>
|
</p>
|
||||||
<p class="FactDatum-value FactDataTable-column" style="word-break: break-all">
|
<p class="FactDataTable-column FactDatum-value" style="word-break: break-all">
|
||||||
{{fact.value2}}
|
{{fact.value2}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="FactDataTable-row FactDatum" ng-class="{'FactDatum--divergent': fact.isDivergent }" ng-repeat="fact in group.facts" ng-unless="group.isFactArray" data-fact="{{fact}}">
|
|
||||||
<p class="FactDataTable-column FactDatum-keyName">
|
|
||||||
{{fact.keyName}}
|
|
||||||
</p>
|
|
||||||
<p class="FactDataTable-column FactDatum-value" style="word-break: break-all">
|
|
||||||
{{fact.value1}}
|
|
||||||
</p>
|
|
||||||
<p class="FactDataTable-column FactDatum-value" style="word-break: break-all">
|
|
||||||
{{fact.value2}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ export default {
|
|||||||
var hostIds = $route.current.params.hosts.split(',');
|
var hostIds = $route.current.params.hosts.split(',');
|
||||||
var moduleParam = $location.search().module || 'packages';
|
var moduleParam = $location.search().module || 'packages';
|
||||||
|
|
||||||
var leftDate = searchDateRange('2015-05-26');
|
var leftDate = searchDateRange('yesterday');
|
||||||
var rightDate = searchDateRange('2015-05-26');
|
var rightDate = searchDateRange();
|
||||||
|
|
||||||
if (hostIds.length === 1) {
|
if (hostIds.length === 1) {
|
||||||
hostIds = hostIds.concat(hostIds[0]);
|
hostIds = hostIds.concat(hostIds[0]);
|
||||||
@@ -30,13 +30,23 @@ export default {
|
|||||||
|
|
||||||
var data =
|
var data =
|
||||||
getDataForComparison(hostIds, moduleParam, leftDate, rightDate).
|
getDataForComparison(hostIds, moduleParam, leftDate, rightDate).
|
||||||
thenThru(function(factData) {
|
thenAll(function(factDataAndModules) {
|
||||||
factData.leftDate = leftDate;
|
var moduleOptions = factDataAndModules[0];
|
||||||
factData.rightDate = rightDate;
|
var factResponses = factDataAndModules[1];
|
||||||
factData.moduleName = moduleParam;
|
var factData = _.pluck(factResponses, 'fact');
|
||||||
return factData;
|
|
||||||
})
|
factData.leftSearchRange = leftDate;
|
||||||
.value();
|
factData.rightSearchRange = rightDate;
|
||||||
|
|
||||||
|
factData.leftScanDate = moment(factResponses[0].timestamp);
|
||||||
|
factData.rightScanDate = moment(factResponses[0].timestamp);
|
||||||
|
|
||||||
|
factData.moduleName = moduleParam;
|
||||||
|
factData.moduleOptions = moduleOptions;
|
||||||
|
|
||||||
|
return factData;
|
||||||
|
}, true)
|
||||||
|
.value();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|
||||||
@@ -48,8 +58,8 @@ export default {
|
|||||||
'Rest',
|
'Rest',
|
||||||
'GetBasePath',
|
'GetBasePath',
|
||||||
function($route, $q, rest, getBasePath) {
|
function($route, $q, rest, getBasePath) {
|
||||||
if ($route.current.params.inventory) {
|
if ($route.current.params.hasModelKey('inventory')) {
|
||||||
return $q.when(true);
|
return $q.when($route.current.params.model.inventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
var inventoryId = $route.current.params.inventory;
|
var inventoryId = $route.current.params.inventory;
|
||||||
@@ -62,17 +72,17 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
filters:
|
hosts:
|
||||||
[ '$route',
|
[ '$route',
|
||||||
'$q',
|
'$q',
|
||||||
'Rest',
|
'Rest',
|
||||||
'GetBasePath',
|
'GetBasePath',
|
||||||
function($route, $q, rest, getBasePath) {
|
function($route, $q, rest, getBasePath) {
|
||||||
if ($route.current.params.hosts) {
|
if ($route.current.params.hasModelKey('hosts')) {
|
||||||
return $q.when(true);
|
return $q.when($route.current.params.model.hosts);
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostIds = $route.current.params.filters.split(',');
|
var hostIds = $route.current.params.hosts.split(',');
|
||||||
|
|
||||||
var hosts =
|
var hosts =
|
||||||
hostIds.map(function(hostId) {
|
hostIds.map(function(hostId) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ describeModule(systemTracking.name)
|
|||||||
"module" : "package",
|
"module" : "package",
|
||||||
"timestamp": '2015-05-07T14:57:37',
|
"timestamp": '2015-05-07T14:57:37',
|
||||||
"related" : {
|
"related" : {
|
||||||
"fact_view" : "/api/v1/hosts/1/fact_view/?module=packages&datetime=2015-05-07T14%3A57%3A37Z"
|
"fact_view" : "/hosts/1/fact_versions/?module=packages&from=2015-05-05T00:00:00-04:00&to=2015-05-06T00:00:00-04:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
result = {
|
result = {
|
||||||
@@ -46,7 +46,10 @@ describeModule(systemTracking.name)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var actual = service.getFacts(version);
|
var actual = service.getFacts(version)
|
||||||
|
.then(function(response) {
|
||||||
|
return response.fact;
|
||||||
|
});
|
||||||
|
|
||||||
restStub.succeedAt(version.related.fact_view, result);
|
restStub.succeedAt(version.related.fact_view, result);
|
||||||
restStub.flush();
|
restStub.flush();
|
||||||
|
|||||||
Reference in New Issue
Block a user