mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Merge branch 'master' into fix-license_2
This commit is contained in:
commit
9f48f127db
8
Makefile
8
Makefile
@ -413,11 +413,11 @@ rpm-build/$(RPM_NVR).noarch.rpm: rpm-build/$(RPM_NVR).src.rpm
|
||||
mock-rpm: rpmtar rpm-build/$(RPM_NVR).noarch.rpm
|
||||
|
||||
ifeq ($(OFFICIAL),yes)
|
||||
rpm-build/$(GPG_FILE): rpm-build
|
||||
gpg --export -a "${GPG_KEY}" > "$@"
|
||||
rpm-build/$(GPG_FILE): rpm-build
|
||||
gpg --export -a "${GPG_KEY}" > "$@"
|
||||
|
||||
rpm-sign: rpm-build/$(GPG_FILE) rpmtar rpm-build/$(RPM_NVR).noarch.rpm
|
||||
rpm --define "_signature gpg" --define "_gpg_name $(GPG_KEY)" --addsign rpm-build/$(RPM_NVR).noarch.rpm
|
||||
rpm-sign: rpm-build/$(GPG_FILE) rpmtar rpm-build/$(RPM_NVR).noarch.rpm
|
||||
rpm --define "_signature gpg" --define "_gpg_name $(GPG_KEY)" --addsign rpm-build/$(RPM_NVR).noarch.rpm
|
||||
endif
|
||||
|
||||
deb-build/$(SDIST_TAR_NAME):
|
||||
|
||||
@ -273,7 +273,7 @@ class ApiV1ConfigView(APIView):
|
||||
mongodb_control.delay('stop')
|
||||
except OSError:
|
||||
pass
|
||||
return Response()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
class DashboardView(APIView):
|
||||
|
||||
|
||||
@ -182,6 +182,7 @@ class FactVersion(Document):
|
||||
meta = {
|
||||
'indexes': [
|
||||
'-timestamp',
|
||||
'module'
|
||||
'module',
|
||||
'host',
|
||||
]
|
||||
}
|
||||
|
||||
@ -31,53 +31,48 @@ class CleanupFacts(object):
|
||||
# pivot -= granularity
|
||||
# group by host
|
||||
def cleanup(self, older_than_abs, granularity, module=None):
|
||||
flag_delete_all = False
|
||||
fact_oldest = FactVersion.objects.all().order_by('timestamp').first()
|
||||
if not fact_oldest:
|
||||
return 0
|
||||
|
||||
kv = {
|
||||
'timestamp__lte': older_than_abs
|
||||
}
|
||||
if module:
|
||||
kv['module'] = module
|
||||
|
||||
# Special case, granularity=0x where x is d, w, or y
|
||||
# The intent is to delete all facts < older_than_abs
|
||||
if granularity == relativedelta():
|
||||
flag_delete_all = True
|
||||
return FactVersion.objects.filter(**kv).order_by('-timestamp').delete()
|
||||
|
||||
total = 0
|
||||
|
||||
date_pivot = older_than_abs
|
||||
while date_pivot > fact_oldest.timestamp:
|
||||
date_pivot_next = date_pivot - granularity
|
||||
|
||||
# For the current time window.
|
||||
# Delete all facts expect the fact that matches the largest timestamp.
|
||||
kv = {
|
||||
'timestamp__lte': date_pivot
|
||||
}
|
||||
if not flag_delete_all:
|
||||
kv['timestamp__gt'] = date_pivot_next
|
||||
if module:
|
||||
kv['module'] = module
|
||||
|
||||
version_objs = FactVersion.objects.filter(**kv).order_by('-timestamp')
|
||||
|
||||
if flag_delete_all:
|
||||
total = version_objs.delete()
|
||||
break
|
||||
|
||||
# Transform array -> {host_id} = [<fact_version>, <fact_version>, ...]
|
||||
# TODO: If this set gets large then we can use mongo to transform the data set for us.
|
||||
host_ids = {}
|
||||
for obj in version_objs:
|
||||
k = obj.host.id
|
||||
if k not in host_ids:
|
||||
host_ids[k] = []
|
||||
host_ids[k].append(obj)
|
||||
|
||||
for k in host_ids:
|
||||
ids = [fact.id for fact in host_ids[k]]
|
||||
fact_ids = [fact.fact.id for fact in host_ids[k]]
|
||||
# Remove 1 entry
|
||||
ids.pop()
|
||||
fact_ids.pop()
|
||||
# delete the rest
|
||||
count = FactVersion.objects.filter(id__in=ids).delete()
|
||||
# FIXME: if this crashes here then we are inconsistent
|
||||
count = Fact.objects.filter(id__in=fact_ids).delete()
|
||||
fact_version_objs = FactVersion.objects.filter(**kv).order_by('-timestamp').limit(1)
|
||||
if fact_version_objs:
|
||||
fact_version_obj = fact_version_objs[0]
|
||||
kv = {
|
||||
'timestamp__lt': fact_version_obj.timestamp,
|
||||
'timestamp__gt': date_pivot_next
|
||||
}
|
||||
if module:
|
||||
kv['module'] = module
|
||||
count = FactVersion.objects.filter(**kv).delete()
|
||||
# FIXME: These two deletes should be a transaction
|
||||
count = Fact.objects.filter(**kv).delete()
|
||||
total += count
|
||||
|
||||
date_pivot = date_pivot_next
|
||||
|
||||
@ -21,6 +21,7 @@ __all__ = ['CommandTest','CleanupFactsUnitTest', 'CleanupFactsCommandFunctionalT
|
||||
class CleanupFactsCommandFunctionalTest(BaseCommandMixin, BaseTest, MongoDBRequired):
|
||||
def setUp(self):
|
||||
super(CleanupFactsCommandFunctionalTest, self).setUp()
|
||||
self.create_test_license_file()
|
||||
self.builder = FactScanBuilder()
|
||||
self.builder.add_fact('ansible', TEST_FACT_ANSIBLE)
|
||||
|
||||
@ -55,6 +56,10 @@ class CleanupFactsCommandFunctionalTest(BaseCommandMixin, BaseTest, MongoDBRequi
|
||||
self.assertEqual(stdout, 'Deleted 25 facts.\n')
|
||||
|
||||
class CommandTest(BaseTest):
|
||||
def setUp(self):
|
||||
super(CommandTest, self).setUp()
|
||||
self.create_test_license_file()
|
||||
|
||||
@mock.patch('awx.main.management.commands.cleanup_facts.CleanupFacts.run')
|
||||
def test_parameters_ok(self, run):
|
||||
|
||||
@ -149,11 +154,11 @@ class CleanupFactsUnitTest(BaseCommandMixin, BaseTest, MongoDBRequired):
|
||||
|
||||
self.builder = FactScanBuilder()
|
||||
self.builder.add_fact('ansible', TEST_FACT_ANSIBLE)
|
||||
self.builder.add_fact('packages', TEST_FACT_PACKAGES)
|
||||
self.builder.build(scan_count=20, host_count=10)
|
||||
|
||||
'''
|
||||
Create 10 hosts with 20 facts each. A single fact a year for 20 years.
|
||||
After cleanup, there should be 10 facts for each host.
|
||||
Create 10 hosts with 40 facts each. After cleanup, there should be 20 facts for each host.
|
||||
Then ensure the correct facts are deleted.
|
||||
'''
|
||||
def test_cleanup_logic(self):
|
||||
@ -162,17 +167,18 @@ class CleanupFactsUnitTest(BaseCommandMixin, BaseTest, MongoDBRequired):
|
||||
granularity = relativedelta(years=2)
|
||||
|
||||
deleted_count = cleanup_facts.cleanup(self.builder.get_timestamp(0), granularity)
|
||||
self.assertEqual(deleted_count, (self.builder.get_scan_count() * self.builder.get_host_count()) / 2)
|
||||
self.assertEqual(deleted_count, 2 * (self.builder.get_scan_count() * self.builder.get_host_count()) / 2)
|
||||
|
||||
# Check the number of facts per host
|
||||
for host in self.builder.get_hosts():
|
||||
count = FactVersion.objects.filter(host=host).count()
|
||||
self.assertEqual(count, self.builder.get_scan_count() / 2, "should have half the number of FactVersion per host for host %s")
|
||||
scan_count = (2 * self.builder.get_scan_count()) / 2
|
||||
self.assertEqual(count, scan_count)
|
||||
|
||||
count = Fact.objects.filter(host=host).count()
|
||||
self.assertEqual(count, self.builder.get_scan_count() / 2, "should have half the number of Fact per host")
|
||||
self.assertEqual(count, scan_count)
|
||||
|
||||
# Ensure that only 1 fact exists per granularity time
|
||||
# Ensure that only 2 facts (ansible and packages) exists per granularity time
|
||||
date_pivot = self.builder.get_timestamp(0)
|
||||
for host in self.builder.get_hosts():
|
||||
while date_pivot > fact_oldest.timestamp:
|
||||
@ -183,11 +189,50 @@ class CleanupFactsUnitTest(BaseCommandMixin, BaseTest, MongoDBRequired):
|
||||
'host': host,
|
||||
}
|
||||
count = FactVersion.objects.filter(**kv).count()
|
||||
self.assertEqual(count, 1, "should only be 1 FactVersion per the 2 year granularity")
|
||||
self.assertEqual(count, 2, "should only be 2 FactVersion per the 2 year granularity")
|
||||
count = Fact.objects.filter(**kv).count()
|
||||
self.assertEqual(count, 1, "should only be 1 Fact per the 2 year granularity")
|
||||
self.assertEqual(count, 2, "should only be 2 Fact per the 2 year granularity")
|
||||
date_pivot = date_pivot_next
|
||||
|
||||
'''
|
||||
Create 10 hosts with 40 facts each. After cleanup, there should be 30 facts for each host.
|
||||
Then ensure the correct facts are deleted.
|
||||
'''
|
||||
def test_cleanup_module(self):
|
||||
cleanup_facts = CleanupFacts()
|
||||
fact_oldest = FactVersion.objects.all().order_by('timestamp').first()
|
||||
granularity = relativedelta(years=2)
|
||||
|
||||
deleted_count = cleanup_facts.cleanup(self.builder.get_timestamp(0), granularity, module='ansible')
|
||||
self.assertEqual(deleted_count, (self.builder.get_scan_count() * self.builder.get_host_count()) / 2)
|
||||
|
||||
# Check the number of facts per host
|
||||
for host in self.builder.get_hosts():
|
||||
count = FactVersion.objects.filter(host=host).count()
|
||||
self.assertEqual(count, 30)
|
||||
|
||||
count = Fact.objects.filter(host=host).count()
|
||||
self.assertEqual(count, 30)
|
||||
|
||||
# Ensure that only 1 ansible fact exists per granularity time
|
||||
date_pivot = self.builder.get_timestamp(0)
|
||||
for host in self.builder.get_hosts():
|
||||
while date_pivot > fact_oldest.timestamp:
|
||||
date_pivot_next = date_pivot - granularity
|
||||
kv = {
|
||||
'timestamp__lte': date_pivot,
|
||||
'timestamp__gt': date_pivot_next,
|
||||
'host': host,
|
||||
'module': 'ansible',
|
||||
}
|
||||
count = FactVersion.objects.filter(**kv).count()
|
||||
self.assertEqual(count, 1)
|
||||
count = Fact.objects.filter(**kv).count()
|
||||
self.assertEqual(count, 1)
|
||||
date_pivot = date_pivot_next
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -440,6 +440,10 @@ var tower = angular.module('Tower', [
|
||||
}
|
||||
}).
|
||||
|
||||
when('/inventories/:inventory_id/job_templates/', {
|
||||
redirectTo: '/inventories/:inventory_id'
|
||||
}).
|
||||
|
||||
when('/inventories/:inventory_id/job_templates/:template_id', {
|
||||
name: 'inventoryJobTemplateEdit',
|
||||
templateUrl: urlPrefix + 'partials/job_templates.html',
|
||||
|
||||
@ -173,6 +173,7 @@ export default
|
||||
// ignore
|
||||
}
|
||||
Alert('License Accepted', 'The Ansible Tower license was updated. To view or update license information in the future choose View License from the Account menu.','alert-info');
|
||||
$rootScope.features = undefined;
|
||||
$location.path('/home');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
|
||||
@media screen and (min-width: (@menu-breakpoint + 1px)) {
|
||||
padding: 0 20px;
|
||||
padding-right: 23px;
|
||||
}
|
||||
|
||||
&-menuContainer {
|
||||
|
||||
@ -14,7 +14,7 @@ export default ['$route', '$rootScope', function($route, $rootScope) {
|
||||
});
|
||||
|
||||
scope.$on('$routeChangeSuccess', function(e, nextRoute) {
|
||||
if (nextRoute.$$route.name === routeName) {
|
||||
if (nextRoute.$$route && nextRoute.$$route.name === routeName) {
|
||||
element.addClass('MenuItem--active');
|
||||
} else {
|
||||
element.removeClass('MenuItem--active');
|
||||
|
||||
@ -14,7 +14,7 @@ export default ['$scope', '$filter',
|
||||
}
|
||||
|
||||
var sparkData =
|
||||
recentJobs.map(function(job) {
|
||||
_.sortBy(recentJobs.map(function(job) {
|
||||
|
||||
var data = {};
|
||||
|
||||
@ -28,10 +28,10 @@ export default ['$scope', '$filter',
|
||||
|
||||
data.jobId = job.id;
|
||||
data.smartStatus = job.status;
|
||||
data.finished = $filter('longDate')(job.finished);
|
||||
data.finished = $filter('longDate')(job.finished) || "running";
|
||||
|
||||
return data;
|
||||
});
|
||||
}), "finished").reverse();
|
||||
|
||||
$scope.sparkArray = _.pluck(sparkData, 'value');
|
||||
$scope.jobIds = _.pluck(sparkData, 'jobId');
|
||||
|
||||
@ -18,10 +18,13 @@ export default [ function() {
|
||||
//capitalize first letter
|
||||
if (status) {
|
||||
status = status.charAt(0).toUpperCase() + status.slice(1);
|
||||
return "<div class=\"smart-status-tooltip\">Job ID: " +
|
||||
var tooltip = "<div class=\"smart-status-tooltip\">Job ID: " +
|
||||
options.userOptions.tooltipValueLookups.jobs[point.offset] +
|
||||
"<br>Status: <span style=\"color: " + point.color + "\">●</span>"+status+
|
||||
"<br>Finished: " + finished +"</div>" ;
|
||||
"<br>Status: <span style=\"color: " + point.color + "\">●</span>"+status;
|
||||
if (finished !== "running") {
|
||||
tooltip += "<br>Finished: " + finished +"</div>" ;
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -9,7 +9,9 @@ import compareFlatFacts from './compare-facts/flat';
|
||||
|
||||
export function compareFacts(module, facts) {
|
||||
if (module.displayType === 'nested') {
|
||||
return compareNestedFacts(facts);
|
||||
return { factData: compareNestedFacts(facts),
|
||||
isNestedDisplay: true
|
||||
};
|
||||
} else {
|
||||
// For flat structures we compare left-to-right, then right-to-left to
|
||||
// make sure we get a good comparison between both hosts
|
||||
|
||||
@ -127,8 +127,8 @@ export function formatFacts(diffedResults) {
|
||||
}
|
||||
|
||||
export function findFacts(factData) {
|
||||
var rightData = factData[0];
|
||||
var leftData = factData[1];
|
||||
var rightData = factData[0].facts;
|
||||
var leftData = factData[1].facts;
|
||||
|
||||
function factObject(keyPath, key, leftValue, rightValue) {
|
||||
var obj =
|
||||
|
||||
@ -45,11 +45,10 @@ function controller($rootScope,
|
||||
$scope.leftHostname = hosts[0].name;
|
||||
$scope.rightHostname = hosts.length > 1 ? hosts[1].name : hosts[0].name;
|
||||
|
||||
function reloadData(params, initialData) {
|
||||
function reloadData(params) {
|
||||
|
||||
searchConfig = _.assign({}, searchConfig, params);
|
||||
|
||||
var factData = initialData;
|
||||
var leftRange = searchConfig.leftRange;
|
||||
var rightRange = searchConfig.rightRange;
|
||||
var activeModule = searchConfig.module;
|
||||
@ -80,9 +79,6 @@ function controller($rootScope,
|
||||
// arrays in index 1
|
||||
//
|
||||
|
||||
// Save the position of the data so we
|
||||
// don't lose it later
|
||||
|
||||
var wrappedFacts =
|
||||
facts.map(function(facts, index) {
|
||||
return { position: index === 0 ? 'left' : 'right',
|
||||
|
||||
@ -1984,3 +1984,7 @@ tr td button i {
|
||||
.job-stdout-panel {
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ body {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-weight: 400;
|
||||
color: @black;
|
||||
padding-top: 75px;
|
||||
padding-top: 58px;
|
||||
}
|
||||
|
||||
#main-menu-container {
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<div class="row">
|
||||
<div class="panel panel-default job-stdout-panel">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{{ job.name }} standard out</h3>
|
||||
<h3 class="panel-title">Standard Out</h3>
|
||||
</div>
|
||||
<div class="panel-body stdout-panel-body">
|
||||
<div class="row">
|
||||
|
||||
@ -146,7 +146,7 @@
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{{ job.name }} standard out</h3>
|
||||
<h3 class="panel-title">Standard Out</h3>
|
||||
</div>
|
||||
<div class="panel-body stdout-panel-body">
|
||||
<div class="row">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user