mirror of
https://github.com/ansible/awx.git
synced 2026-02-21 13:10:11 -03:30
Adding initial instance group policies
and policy evaluation planner
This commit is contained in:
@@ -4011,7 +4011,8 @@ class InstanceGroupSerializer(BaseSerializer):
|
|||||||
model = InstanceGroup
|
model = InstanceGroup
|
||||||
fields = ("id", "type", "url", "related", "name", "created", "modified",
|
fields = ("id", "type", "url", "related", "name", "created", "modified",
|
||||||
"capacity", "committed_capacity", "consumed_capacity",
|
"capacity", "committed_capacity", "consumed_capacity",
|
||||||
"percent_capacity_remaining", "jobs_running", "instances", "controller")
|
"percent_capacity_remaining", "jobs_running", "instances", "controller",
|
||||||
|
"policy_instance_percentage", "policy_instance_minimum")
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(InstanceGroupSerializer, self).get_related(obj)
|
res = super(InstanceGroupSerializer, self).get_related(obj)
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ class Command(BaseCommand):
|
|||||||
help='Comma-Delimited Hosts to add to the Queue')
|
help='Comma-Delimited Hosts to add to the Queue')
|
||||||
parser.add_argument('--controller', dest='controller', type=str,
|
parser.add_argument('--controller', dest='controller', type=str,
|
||||||
default='', help='The controlling group (makes this an isolated group)')
|
default='', help='The controlling group (makes this an isolated group)')
|
||||||
|
parser.add_argument('--instance_percent', dest='instance_percent', type=int, default=0,
|
||||||
|
help='The percentage of active instances that will be assigned to this group'),
|
||||||
|
parser.add_argument('--instance_minimum', dest='instance_minimum', type=int, default=0,
|
||||||
|
help='The minimum number of instance that will be retained for this group from available instances')
|
||||||
|
|
||||||
def handle(self, **options):
|
def handle(self, **options):
|
||||||
queuename = options.get('queuename')
|
queuename = options.get('queuename')
|
||||||
@@ -38,7 +42,9 @@ class Command(BaseCommand):
|
|||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
print("Creating instance group {}".format(queuename))
|
print("Creating instance group {}".format(queuename))
|
||||||
ig = InstanceGroup(name=queuename)
|
ig = InstanceGroup(name=queuename,
|
||||||
|
policy_instance_percentage=options.get('instance_percent'),
|
||||||
|
policy_instance_minimum=options.get('instance_minimum'))
|
||||||
if control_ig:
|
if control_ig:
|
||||||
ig.controller = control_ig
|
ig.controller = control_ig
|
||||||
ig.save()
|
ig.save()
|
||||||
@@ -60,5 +66,7 @@ class Command(BaseCommand):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
print("Instance already registered {}".format(instance[0].hostname))
|
print("Instance already registered {}".format(instance[0].hostname))
|
||||||
|
ig.policy_instance_list = instance_list
|
||||||
|
ig.save()
|
||||||
if changed:
|
if changed:
|
||||||
print('(changed: True)')
|
print('(changed: True)')
|
||||||
|
|||||||
30
awx/main/migrations/0013_v330_instancegroup_policies.py
Normal file
30
awx/main/migrations/0013_v330_instancegroup_policies.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import awx.main.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0008_v320_drop_v1_credential_fields'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='instancegroup',
|
||||||
|
name='policy_instance_list',
|
||||||
|
field=awx.main.fields.JSONField(default=[], help_text='List of exact-match Instances that will always be automatically assigned to this group', blank=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='instancegroup',
|
||||||
|
name='policy_instance_minimum',
|
||||||
|
field=models.IntegerField(default=0, help_text='Static minimum number of Instances to automatically assign to this group'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='instancegroup',
|
||||||
|
name='policy_instance_percentage',
|
||||||
|
field=models.IntegerField(default=0, help_text='Percentage of Instances to automatically assign to this group'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -12,6 +12,7 @@ from solo.models import SingletonModel
|
|||||||
|
|
||||||
from awx.api.versioning import reverse
|
from awx.api.versioning import reverse
|
||||||
from awx.main.managers import InstanceManager, InstanceGroupManager
|
from awx.main.managers import InstanceManager, InstanceGroupManager
|
||||||
|
from awx.main.fields import JSONField
|
||||||
from awx.main.models.inventory import InventoryUpdate
|
from awx.main.models.inventory import InventoryUpdate
|
||||||
from awx.main.models.jobs import Job
|
from awx.main.models.jobs import Job
|
||||||
from awx.main.models.projects import ProjectUpdate
|
from awx.main.models.projects import ProjectUpdate
|
||||||
@@ -88,6 +89,19 @@ class InstanceGroup(models.Model):
|
|||||||
default=None,
|
default=None,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
policy_instance_percentage = models.IntegerField(
|
||||||
|
default=0,
|
||||||
|
help_text=_("Percentage of Instances to automatically assign to this group")
|
||||||
|
)
|
||||||
|
policy_instance_minimum = models.IntegerField(
|
||||||
|
default=0,
|
||||||
|
help_text=_("Static minimum number of Instances to automatically assign to this group")
|
||||||
|
)
|
||||||
|
policy_instance_list = JSONField(
|
||||||
|
default=[],
|
||||||
|
blank=True,
|
||||||
|
help_text=_("List of exact-match Instances that will always be automatically assigned to this group")
|
||||||
|
)
|
||||||
|
|
||||||
def get_absolute_url(self, request=None):
|
def get_absolute_url(self, request=None):
|
||||||
return reverse('api:instance_group_detail', kwargs={'pk': self.pk}, request=request)
|
return reverse('api:instance_group_detail', kwargs={'pk': self.pk}, request=request)
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
from collections import OrderedDict
|
import codecs
|
||||||
|
from collections import OrderedDict, namedtuple
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
import cStringIO
|
import cStringIO
|
||||||
import functools
|
import functools
|
||||||
@@ -131,6 +132,43 @@ def inform_cluster_of_shutdown(*args, **kwargs):
|
|||||||
logger.exception('Encountered problem with normal shutdown signal.')
|
logger.exception('Encountered problem with normal shutdown signal.')
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task(bind=True, queue='tower', base=LogErrorsTask)
|
||||||
|
def apply_cluster_membership_policies(self):
|
||||||
|
considered_instances = Instance.objects.all().order_by('id').only('id')
|
||||||
|
total_instances = considered_instances.count()
|
||||||
|
actual_groups = []
|
||||||
|
actual_instances = []
|
||||||
|
Group = namedtuple('Group', ['obj', 'instances'])
|
||||||
|
Instance = namedtuple('Instance', ['obj', 'groups'])
|
||||||
|
# Process policy instance list first, these will represent manually managed instances
|
||||||
|
# that will not go through automatic policy determination
|
||||||
|
for ig in InstanceGroup.objects.all():
|
||||||
|
group_actual = Group(obj=ig, instances=[])
|
||||||
|
for i in ig.policy_instance_list:
|
||||||
|
group_actual.instances.append(i)
|
||||||
|
if i in considered_instances:
|
||||||
|
considered_instances.remove(i)
|
||||||
|
actual_groups.append(group_actual)
|
||||||
|
# Process Instance minimum policies next, since it represents a concrete lower bound to the
|
||||||
|
# number of instances to make available to instance groups
|
||||||
|
for i in considered_instances:
|
||||||
|
instance_actual = Instance(obj=i, groups=[])
|
||||||
|
for g in sorted(actual_groups, cmp=lambda x,y: len(x.instances) - len(y.instances)):
|
||||||
|
if len(g.instances) < g.obj.policy_instance_minimum:
|
||||||
|
g.instances.append(instance_actual.obj.id)
|
||||||
|
instance_actual.groups.append(g.obj.id)
|
||||||
|
break
|
||||||
|
actual_instances.append(instance_actual)
|
||||||
|
# Finally process instance policy percentages
|
||||||
|
for i in sorted(actual_instances, cmp=lambda x,y: len(x.groups) - len(y.groups)):
|
||||||
|
for g in sorted(actual_groups, cmp=lambda x,y: len(x.instances) - len(y.instances)):
|
||||||
|
if 100 * float(len(g.instances)) / total_instances < g.obj.policy_instance_percentage:
|
||||||
|
g.instances.append(i.obj.id)
|
||||||
|
i.groups.append(g.obj.id)
|
||||||
|
break
|
||||||
|
# Next step
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue='tower_broadcast_all', bind=True, base=LogErrorsTask)
|
@shared_task(queue='tower_broadcast_all', bind=True, base=LogErrorsTask)
|
||||||
def handle_setting_changes(self, setting_keys):
|
def handle_setting_changes(self, setting_keys):
|
||||||
orig_len = len(setting_keys)
|
orig_len = len(setting_keys)
|
||||||
|
|||||||
Reference in New Issue
Block a user