mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 14:57:39 -02:30
Make the necessary changes to the models
- remove InstanceGroup.controller - remove Instance.last_isolated_check - remove .is_isolated and .is_controller methods/properties - remove .choose_online_controller_node() method - remove .supports_isolation() and replace with .can_run_containerized - simplify .can_run_containerized
This commit is contained in:
26
awx/main/migrations/0139_isolated_removal.py
Normal file
26
awx/main/migrations/0139_isolated_removal.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Generated by Django 2.2.16 on 2021-04-21 15:02
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0138_custom_inventory_scripts_removal'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='instance',
|
||||||
|
name='last_isolated_check',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='instancegroup',
|
||||||
|
name='controller',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='unifiedjob',
|
||||||
|
name='controller_node',
|
||||||
|
field=models.TextField(blank=True, default='', editable=False, help_text='The instance that managed the execution environment.'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -146,10 +146,6 @@ class AdHocCommand(UnifiedJob, JobNotificationMixin):
|
|||||||
|
|
||||||
return RunAdHocCommand
|
return RunAdHocCommand
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_isolation(cls):
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_container_group_task(self):
|
def is_container_group_task(self):
|
||||||
return bool(self.instance_group and self.instance_group.is_container_group)
|
return bool(self.instance_group and self.instance_group.is_container_group)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# Copyright (c) 2015 Ansible, Inc.
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
|
||||||
import random
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
@@ -63,10 +62,6 @@ class Instance(HasPolicyEditsMixin, BaseModel):
|
|||||||
)
|
)
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
last_isolated_check = models.DateTimeField(
|
|
||||||
null=True,
|
|
||||||
editable=False,
|
|
||||||
)
|
|
||||||
version = models.CharField(max_length=120, blank=True)
|
version = models.CharField(max_length=120, blank=True)
|
||||||
capacity = models.PositiveIntegerField(
|
capacity = models.PositiveIntegerField(
|
||||||
default=100,
|
default=100,
|
||||||
@@ -128,20 +123,12 @@ class Instance(HasPolicyEditsMixin, BaseModel):
|
|||||||
def jobs_total(self):
|
def jobs_total(self):
|
||||||
return UnifiedJob.objects.filter(execution_node=self.hostname).count()
|
return UnifiedJob.objects.filter(execution_node=self.hostname).count()
|
||||||
|
|
||||||
def is_lost(self, ref_time=None, isolated=False):
|
def is_lost(self, ref_time=None):
|
||||||
if ref_time is None:
|
if ref_time is None:
|
||||||
ref_time = now()
|
ref_time = now()
|
||||||
grace_period = 120
|
grace_period = 120
|
||||||
if isolated:
|
|
||||||
grace_period = settings.AWX_ISOLATED_PERIODIC_CHECK * 2
|
|
||||||
return self.modified < ref_time - timedelta(seconds=grace_period)
|
return self.modified < ref_time - timedelta(seconds=grace_period)
|
||||||
|
|
||||||
def is_controller(self):
|
|
||||||
return Instance.objects.filter(rampart_groups__controller__instances=self).exists()
|
|
||||||
|
|
||||||
def is_isolated(self):
|
|
||||||
return self.rampart_groups.filter(controller__isnull=False).exists()
|
|
||||||
|
|
||||||
def refresh_capacity(self):
|
def refresh_capacity(self):
|
||||||
if settings.IS_K8S:
|
if settings.IS_K8S:
|
||||||
self.capacity = self.cpu = self.memory = self.cpu_capacity = self.mem_capacity = 0 # noqa
|
self.capacity = self.cpu = self.memory = self.cpu_capacity = self.mem_capacity = 0 # noqa
|
||||||
@@ -185,15 +172,6 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin):
|
|||||||
editable=False,
|
editable=False,
|
||||||
help_text=_('Instances that are members of this InstanceGroup'),
|
help_text=_('Instances that are members of this InstanceGroup'),
|
||||||
)
|
)
|
||||||
controller = models.ForeignKey(
|
|
||||||
'InstanceGroup',
|
|
||||||
related_name='controlled_groups',
|
|
||||||
help_text=_('Instance Group to remotely control this group.'),
|
|
||||||
editable=False,
|
|
||||||
default=None,
|
|
||||||
null=True,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
is_container_group = models.BooleanField(default=False)
|
is_container_group = models.BooleanField(default=False)
|
||||||
credential = models.ForeignKey(
|
credential = models.ForeignKey(
|
||||||
'Credential',
|
'Credential',
|
||||||
@@ -215,7 +193,7 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin):
|
|||||||
default=[], blank=True, help_text=_("List of exact-match Instances that will always be automatically assigned to this group")
|
default=[], blank=True, help_text=_("List of exact-match Instances that will always be automatically assigned to this group")
|
||||||
)
|
)
|
||||||
|
|
||||||
POLICY_FIELDS = frozenset(('policy_instance_list', 'policy_instance_minimum', 'policy_instance_percentage', 'controller'))
|
POLICY_FIELDS = frozenset(('policy_instance_list', 'policy_instance_minimum', 'policy_instance_percentage'))
|
||||||
|
|
||||||
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)
|
||||||
@@ -232,14 +210,6 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin):
|
|||||||
def jobs_total(self):
|
def jobs_total(self):
|
||||||
return UnifiedJob.objects.filter(instance_group=self).count()
|
return UnifiedJob.objects.filter(instance_group=self).count()
|
||||||
|
|
||||||
@property
|
|
||||||
def is_controller(self):
|
|
||||||
return self.controlled_groups.exists()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_isolated(self):
|
|
||||||
return bool(self.controller)
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
RelatedJobsMixin
|
RelatedJobsMixin
|
||||||
'''
|
'''
|
||||||
@@ -271,9 +241,6 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin):
|
|||||||
largest_instance = i
|
largest_instance = i
|
||||||
return largest_instance
|
return largest_instance
|
||||||
|
|
||||||
def choose_online_controller_node(self):
|
|
||||||
return random.choice(list(self.controller.instances.filter(capacity__gt=0, enabled=True).values_list('hostname', flat=True)))
|
|
||||||
|
|
||||||
def set_default_policy_fields(self):
|
def set_default_policy_fields(self):
|
||||||
self.policy_instance_list = []
|
self.policy_instance_list = []
|
||||||
self.policy_instance_minimum = 0
|
self.policy_instance_minimum = 0
|
||||||
|
|||||||
@@ -587,10 +587,6 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
|
|||||||
|
|
||||||
return RunJob
|
return RunJob
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_isolation(cls):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _global_timeout_setting(self):
|
def _global_timeout_setting(self):
|
||||||
return 'DEFAULT_JOB_TIMEOUT'
|
return 'DEFAULT_JOB_TIMEOUT'
|
||||||
|
|
||||||
@@ -759,7 +755,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def can_run_containerized(self):
|
def can_run_containerized(self):
|
||||||
return any([ig for ig in self.preferred_instance_groups if ig.is_container_group])
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_container_group_task(self):
|
def is_container_group_task(self):
|
||||||
|
|||||||
@@ -588,7 +588,7 @@ class UnifiedJob(
|
|||||||
blank=True,
|
blank=True,
|
||||||
default='',
|
default='',
|
||||||
editable=False,
|
editable=False,
|
||||||
help_text=_("The instance that managed the isolated execution environment."),
|
help_text=_("The instance that managed the execution environment."),
|
||||||
)
|
)
|
||||||
notifications = models.ManyToManyField(
|
notifications = models.ManyToManyField(
|
||||||
'Notification',
|
'Notification',
|
||||||
@@ -737,10 +737,6 @@ class UnifiedJob(
|
|||||||
def _get_task_class(cls):
|
def _get_task_class(cls):
|
||||||
raise NotImplementedError # Implement in subclasses.
|
raise NotImplementedError # Implement in subclasses.
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_isolation(cls):
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def can_run_containerized(self):
|
def can_run_containerized(self):
|
||||||
return False
|
return False
|
||||||
@@ -1402,12 +1398,11 @@ class UnifiedJob(
|
|||||||
@property
|
@property
|
||||||
def preferred_instance_groups(self):
|
def preferred_instance_groups(self):
|
||||||
"""
|
"""
|
||||||
Return Instance/Rampart Groups preferred by this unified job templates
|
Return Instance/Rampart Groups preferred by this unified job template
|
||||||
"""
|
"""
|
||||||
if not self.unified_job_template:
|
if not self.unified_job_template:
|
||||||
return []
|
return []
|
||||||
template_groups = [x for x in self.unified_job_template.instance_groups.all()]
|
return list(self.unified_job_template.instance_groups.all())
|
||||||
return template_groups
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def global_instance_groups(self):
|
def global_instance_groups(self):
|
||||||
@@ -1467,9 +1462,6 @@ class UnifiedJob(
|
|||||||
def get_queue_name(self):
|
def get_queue_name(self):
|
||||||
return self.controller_node or self.execution_node or get_local_queuename()
|
return self.controller_node or self.execution_node or get_local_queuename()
|
||||||
|
|
||||||
def is_isolated(self):
|
|
||||||
return bool(self.controller_node)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_container_group_task(self):
|
def is_container_group_task(self):
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from datetime import timedelta
|
|||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
import json
|
import json
|
||||||
import random
|
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
@@ -253,14 +252,6 @@ class TaskManager:
|
|||||||
}
|
}
|
||||||
dependencies = [{'type': get_type_for_model(type(t)), 'id': t.id} for t in dependent_tasks]
|
dependencies = [{'type': get_type_for_model(type(t)), 'id': t.id} for t in dependent_tasks]
|
||||||
|
|
||||||
controller_node = None
|
|
||||||
if task.supports_isolation() and rampart_group.controller_id:
|
|
||||||
try:
|
|
||||||
controller_node = rampart_group.choose_online_controller_node()
|
|
||||||
except IndexError:
|
|
||||||
logger.debug("No controllers available in group {} to run {}".format(rampart_group.name, task.log_format))
|
|
||||||
return
|
|
||||||
|
|
||||||
task.status = 'waiting'
|
task.status = 'waiting'
|
||||||
|
|
||||||
(start_status, opts) = task.pre_start()
|
(start_status, opts) = task.pre_start()
|
||||||
@@ -277,38 +268,24 @@ class TaskManager:
|
|||||||
task.send_notification_templates('running')
|
task.send_notification_templates('running')
|
||||||
logger.debug('Transitioning %s to running status.', task.log_format)
|
logger.debug('Transitioning %s to running status.', task.log_format)
|
||||||
schedule_task_manager()
|
schedule_task_manager()
|
||||||
elif not task.supports_isolation() and rampart_group.controller_id:
|
|
||||||
# non-Ansible jobs on isolated instances run on controller
|
|
||||||
task.instance_group = rampart_group.controller
|
|
||||||
task.execution_node = random.choice(list(rampart_group.controller.instances.all().values_list('hostname', flat=True)))
|
|
||||||
logger.debug('Submitting isolated {} to queue {} on node {}.'.format(task.log_format, task.instance_group.name, task.execution_node))
|
|
||||||
elif controller_node:
|
|
||||||
task.instance_group = rampart_group
|
|
||||||
task.execution_node = instance.hostname
|
|
||||||
task.controller_node = controller_node
|
|
||||||
logger.debug('Submitting isolated {} to queue {} controlled by {}.'.format(task.log_format, task.execution_node, controller_node))
|
|
||||||
elif rampart_group.is_container_group:
|
elif rampart_group.is_container_group:
|
||||||
# find one real, non-containerized instance with capacity to
|
# find one real, non-containerized instance with capacity to
|
||||||
# act as the controller for k8s API interaction
|
# act as the controller for k8s API interaction
|
||||||
match = None
|
match = None
|
||||||
for group in InstanceGroup.objects.all():
|
for group in InstanceGroup.objects.filter(is_container_group=False):
|
||||||
if group.is_container_group or group.controller_id:
|
|
||||||
continue
|
|
||||||
match = group.fit_task_to_most_remaining_capacity_instance(task, group.instances.all())
|
match = group.fit_task_to_most_remaining_capacity_instance(task, group.instances.all())
|
||||||
if match:
|
if match:
|
||||||
break
|
break
|
||||||
task.instance_group = rampart_group
|
task.instance_group = rampart_group
|
||||||
if match is None:
|
if match is None:
|
||||||
logger.warn('No available capacity to run containerized <{}>.'.format(task.log_format))
|
logger.warn('No available capacity to run containerized <{}>.'.format(task.log_format))
|
||||||
|
elif task.can_run_containerized and any(ig.is_container_group for ig in task.preferred_instance_groups):
|
||||||
|
task.controller_node = match.hostname
|
||||||
else:
|
else:
|
||||||
if task.supports_isolation():
|
# project updates and inventory updates don't *actually* run in pods, so
|
||||||
task.controller_node = match.hostname
|
# just pick *any* non-containerized host and use it as the execution node
|
||||||
else:
|
task.execution_node = match.hostname
|
||||||
# project updates and inventory updates don't *actually* run in pods,
|
logger.debug('Submitting containerized {} to queue {}.'.format(task.log_format, task.execution_node))
|
||||||
# so just pick *any* non-isolated, non-containerized host and use it
|
|
||||||
# as the execution node
|
|
||||||
task.execution_node = match.hostname
|
|
||||||
logger.debug('Submitting containerized {} to queue {}.'.format(task.log_format, task.execution_node))
|
|
||||||
else:
|
else:
|
||||||
task.instance_group = rampart_group
|
task.instance_group = rampart_group
|
||||||
if instance is not None:
|
if instance is not None:
|
||||||
|
|||||||
Reference in New Issue
Block a user