class InstanceGroup(models.Model): """A model representing a Queue/Group of AWX Instances.""" objects = InstanceGroupManager() name = models.CharField(max_length=250, unique=True) created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) instances = models.ManyToManyField( 'Instance', related_name='rampart_groups', 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) def get_absolute_url(self, request=None): return reverse('api:instance_group_detail', kwargs={'pk': self.pk}, request=request) @property def capacity(self): return sum([inst.capacity for inst in self.instances.all()]) class Meta: app_label = 'main'
class InstanceGroup(models.Model, RelatedJobsMixin): """A model representing a Queue/Group of AWX Instances.""" objects = InstanceGroupManager() name = models.CharField(max_length=250, unique=True) created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) instances = models.ManyToManyField( 'Instance', related_name='rampart_groups', 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) 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): return reverse('api:instance_group_detail', kwargs={'pk': self.pk}, request=request) @property def capacity(self): return sum([inst.capacity for inst in self.instances.all()]) ''' RelatedJobsMixin ''' def _get_related_jobs(self): return UnifiedJob.objects.filter(instance_group=self) class Meta: app_label = 'main'
class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin): """A model representing a Queue/Group of AWX Instances.""" objects = InstanceGroupManager() name = models.CharField(max_length=250, unique=True) created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) instances = models.ManyToManyField( 'Instance', related_name='rampart_groups', 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) 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" )) POLICY_FIELDS = frozenset( ('policy_instance_list', 'policy_instance_minimum', 'policy_instance_percentage', 'controller')) def get_absolute_url(self, request=None): return reverse('api:instance_group_detail', kwargs={'pk': self.pk}, request=request) @property def capacity(self): return sum([inst.capacity for inst in self.instances.all()]) @property def jobs_running(self): return UnifiedJob.objects.filter(status__in=('running', 'waiting'), instance_group=self).count() @property 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 ''' def _get_related_jobs(self): return UnifiedJob.objects.filter(instance_group=self) class Meta: app_label = 'main' def fit_task_to_most_remaining_capacity_instance(self, task): instance_most_capacity = None for i in self.instances.filter(capacity__gt=0, enabled=True).order_by('hostname'): if i.remaining_capacity >= task.task_impact and \ (instance_most_capacity is None or i.remaining_capacity > instance_most_capacity.remaining_capacity): instance_most_capacity = i return instance_most_capacity def find_largest_idle_instance(self): largest_instance = None for i in self.instances.filter(capacity__gt=0, enabled=True).order_by('hostname'): if i.jobs_running == 0: if largest_instance is None: largest_instance = i elif i.capacity > largest_instance.capacity: 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)))
class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin): """A model representing a Queue/Group of AWX Instances.""" objects = InstanceGroupManager() name = models.CharField(max_length=250, unique=True) created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) instances = models.ManyToManyField( 'Instance', related_name='rampart_groups', editable=False, help_text=_('Instances that are members of this InstanceGroup'), ) is_container_group = models.BooleanField(default=False) credential = models.ForeignKey( 'Credential', related_name='%(class)ss', blank=True, null=True, default=None, on_delete=models.SET_NULL, ) pod_spec_override = prevent_search( models.TextField( blank=True, default='', )) 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 = JSONBlob( default=list, 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')) def get_absolute_url(self, request=None): return reverse('api:instance_group_detail', kwargs={'pk': self.pk}, request=request) @property def capacity(self): return sum(inst.capacity for inst in self.instances.all()) @property def jobs_running(self): return UnifiedJob.objects.filter(status__in=('running', 'waiting'), instance_group=self).count() @property def jobs_total(self): return UnifiedJob.objects.filter(instance_group=self).count() ''' RelatedJobsMixin ''' def _get_related_jobs(self): return UnifiedJob.objects.filter(instance_group=self) class Meta: app_label = 'main' @staticmethod def fit_task_to_most_remaining_capacity_instance( task, instances, impact=None, capacity_type=None, add_hybrid_control_cost=False): impact = impact if impact else task.task_impact capacity_type = capacity_type if capacity_type else task.capacity_type instance_most_capacity = None most_remaining_capacity = -1 for i in instances: if i.node_type not in (capacity_type, 'hybrid'): continue would_be_remaining = i.remaining_capacity - impact # hybrid nodes _always_ control their own tasks if add_hybrid_control_cost and i.node_type == 'hybrid': would_be_remaining -= settings.AWX_CONTROL_NODE_TASK_IMPACT if would_be_remaining >= 0 and ( instance_most_capacity is None or would_be_remaining > most_remaining_capacity): instance_most_capacity = i most_remaining_capacity = would_be_remaining return instance_most_capacity @staticmethod def find_largest_idle_instance(instances, capacity_type='execution'): largest_instance = None for i in instances: if i.node_type not in (capacity_type, 'hybrid'): continue if i.jobs_running == 0: if largest_instance is None: largest_instance = i elif i.capacity > largest_instance.capacity: largest_instance = i return largest_instance def set_default_policy_fields(self): self.policy_instance_list = [] self.policy_instance_minimum = 0 self.policy_instance_percentage = 0