From 1819a7963a7117c48a4813a782e3b586e7c141d5 Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Wed, 21 Apr 2021 11:03:38 -0400 Subject: [PATCH] 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 --- awx/main/migrations/0139_isolated_removal.py | 26 ++++++++++++++ awx/main/models/ad_hoc_commands.py | 4 --- awx/main/models/ha.py | 37 ++------------------ awx/main/models/jobs.py | 6 +--- awx/main/models/unified_jobs.py | 14 ++------ awx/main/scheduler/task_manager.py | 37 ++++---------------- 6 files changed, 39 insertions(+), 85 deletions(-) create mode 100644 awx/main/migrations/0139_isolated_removal.py diff --git a/awx/main/migrations/0139_isolated_removal.py b/awx/main/migrations/0139_isolated_removal.py new file mode 100644 index 0000000000..06bd0521cb --- /dev/null +++ b/awx/main/migrations/0139_isolated_removal.py @@ -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.'), + ), + ] diff --git a/awx/main/models/ad_hoc_commands.py b/awx/main/models/ad_hoc_commands.py index 105991a8a0..5ee08857f6 100644 --- a/awx/main/models/ad_hoc_commands.py +++ b/awx/main/models/ad_hoc_commands.py @@ -146,10 +146,6 @@ class AdHocCommand(UnifiedJob, JobNotificationMixin): return RunAdHocCommand - @classmethod - def supports_isolation(cls): - return True - @property def is_container_group_task(self): return bool(self.instance_group and self.instance_group.is_container_group) diff --git a/awx/main/models/ha.py b/awx/main/models/ha.py index b8e5ab27a6..fd5ec56596 100644 --- a/awx/main/models/ha.py +++ b/awx/main/models/ha.py @@ -1,7 +1,6 @@ # Copyright (c) 2015 Ansible, Inc. # All Rights Reserved. -import random from decimal import Decimal from django.core.validators import MinValueValidator @@ -63,10 +62,6 @@ class Instance(HasPolicyEditsMixin, BaseModel): ) created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - last_isolated_check = models.DateTimeField( - null=True, - editable=False, - ) version = models.CharField(max_length=120, blank=True) capacity = models.PositiveIntegerField( default=100, @@ -128,20 +123,12 @@ class Instance(HasPolicyEditsMixin, BaseModel): def jobs_total(self): 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: ref_time = now() grace_period = 120 - if isolated: - grace_period = settings.AWX_ISOLATED_PERIODIC_CHECK * 2 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): if settings.IS_K8S: 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, 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) credential = models.ForeignKey( '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") ) - 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): 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): 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 ''' @@ -271,9 +241,6 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin): largest_instance = i 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): self.policy_instance_list = [] self.policy_instance_minimum = 0 diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 807c276930..2b0bb6fd20 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -587,10 +587,6 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana return RunJob - @classmethod - def supports_isolation(cls): - return True - def _global_timeout_setting(self): return 'DEFAULT_JOB_TIMEOUT' @@ -759,7 +755,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana @property def can_run_containerized(self): - return any([ig for ig in self.preferred_instance_groups if ig.is_container_group]) + return True @property def is_container_group_task(self): diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 6bc85e3162..65c8c8f39b 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -588,7 +588,7 @@ class UnifiedJob( blank=True, default='', editable=False, - help_text=_("The instance that managed the isolated execution environment."), + help_text=_("The instance that managed the execution environment."), ) notifications = models.ManyToManyField( 'Notification', @@ -737,10 +737,6 @@ class UnifiedJob( def _get_task_class(cls): raise NotImplementedError # Implement in subclasses. - @classmethod - def supports_isolation(cls): - return False - @property def can_run_containerized(self): return False @@ -1402,12 +1398,11 @@ class UnifiedJob( @property 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: return [] - template_groups = [x for x in self.unified_job_template.instance_groups.all()] - return template_groups + return list(self.unified_job_template.instance_groups.all()) @property def global_instance_groups(self): @@ -1467,9 +1462,6 @@ class UnifiedJob( def get_queue_name(self): return self.controller_node or self.execution_node or get_local_queuename() - def is_isolated(self): - return bool(self.controller_node) - @property def is_container_group_task(self): return False diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index 0757132cf9..115838c66c 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -6,7 +6,6 @@ from datetime import timedelta import logging import uuid import json -import random from types import SimpleNamespace # Django @@ -253,14 +252,6 @@ class TaskManager: } 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' (start_status, opts) = task.pre_start() @@ -277,38 +268,24 @@ class TaskManager: task.send_notification_templates('running') logger.debug('Transitioning %s to running status.', task.log_format) 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: # find one real, non-containerized instance with capacity to # act as the controller for k8s API interaction match = None - for group in InstanceGroup.objects.all(): - if group.is_container_group or group.controller_id: - continue + for group in InstanceGroup.objects.filter(is_container_group=False): match = group.fit_task_to_most_remaining_capacity_instance(task, group.instances.all()) if match: break task.instance_group = rampart_group if match is None: 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: - if task.supports_isolation(): - task.controller_node = match.hostname - else: - # project updates and inventory updates don't *actually* run in pods, - # 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)) + # project updates and inventory updates don't *actually* run in pods, so + # just pick *any* 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: task.instance_group = rampart_group if instance is not None: