Merge pull request #3893 from AlanCoding/replace_job_origin

Replace JobOrigin model with ActivityStream.action_node field

Reviewed-by: https://github.com/softwarefactory-project-zuul[bot]
This commit is contained in:
softwarefactory-project-zuul[bot]
2019-05-31 11:59:09 +00:00
committed by GitHub
6 changed files with 71 additions and 50 deletions

View File

@@ -4984,7 +4984,7 @@ class ActivityStreamSerializer(BaseSerializer):
class Meta: class Meta:
model = ActivityStream model = ActivityStream
fields = ('*', '-name', '-description', '-created', '-modified', 'timestamp', 'operation', fields = ('*', '-name', '-description', '-created', '-modified', 'timestamp', 'operation',
'changes', 'object1', 'object2', 'object_association', 'object_type') 'changes', 'object1', 'object2', 'object_association', 'action_node', 'object_type')
def get_fields(self): def get_fields(self):
ret = super(ActivityStreamSerializer, self).get_fields() ret = super(ActivityStreamSerializer, self).get_fields()

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-05-14 14:51
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0079_v360_rm_implicit_oauth2_apps'),
]
operations = [
migrations.RemoveField(
model_name='joborigin',
name='instance',
),
migrations.RemoveField(
model_name='joborigin',
name='unified_job',
),
migrations.AddField(
model_name='activitystream',
name='action_node',
field=models.CharField(blank=True, default='', editable=False, help_text='The cluster node the activity took place on.', max_length=512),
),
migrations.DeleteModel(
name='JobOrigin',
),
]

View File

@@ -35,7 +35,7 @@ from awx.main.models.ad_hoc_commands import AdHocCommand # noqa
from awx.main.models.schedules import Schedule # noqa from awx.main.models.schedules import Schedule # noqa
from awx.main.models.activity_stream import ActivityStream # noqa from awx.main.models.activity_stream import ActivityStream # noqa
from awx.main.models.ha import ( # noqa from awx.main.models.ha import ( # noqa
Instance, InstanceGroup, JobOrigin, TowerScheduleState, Instance, InstanceGroup, TowerScheduleState,
) )
from awx.main.models.rbac import ( # noqa from awx.main.models.rbac import ( # noqa
Role, batch_role_ancestor_rebuilding, get_roles_on_resource, Role, batch_role_ancestor_rebuilding, get_roles_on_resource,

View File

@@ -7,6 +7,7 @@ from awx.main.fields import JSONField
# Django # Django
from django.db import models from django.db import models
from django.conf import settings
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@@ -35,6 +36,13 @@ class ActivityStream(models.Model):
timestamp = models.DateTimeField(auto_now_add=True) timestamp = models.DateTimeField(auto_now_add=True)
changes = models.TextField(blank=True) changes = models.TextField(blank=True)
deleted_actor = JSONField(null=True) deleted_actor = JSONField(null=True)
action_node = models.CharField(
blank=True,
default='',
editable=False,
max_length=512,
help_text=_("The cluster node the activity took place on."),
)
object_relationship_type = models.TextField(blank=True) object_relationship_type = models.TextField(blank=True)
object1 = models.TextField() object1 = models.TextField()
@@ -78,7 +86,13 @@ class ActivityStream(models.Model):
def __str__(self): def __str__(self):
operation = self.operation if 'operation' in self.__dict__ else '_delayed_' operation = self.operation if 'operation' in self.__dict__ else '_delayed_'
timestamp = self.timestamp.isoformat() if 'timestamp' in self.__dict__ else '_delayed_' if 'timestamp' in self.__dict__:
if self.timestamp:
timestamp = self.timestamp.isoformat()
else:
timestamp = self.timestamp
else:
timestamp = '_delayed_'
return u'%s-%s-pk=%s' % (operation, timestamp, self.pk) return u'%s-%s-pk=%s' % (operation, timestamp, self.pk)
def get_absolute_url(self, request=None): def get_absolute_url(self, request=None):
@@ -97,4 +111,7 @@ class ActivityStream(models.Model):
if 'update_fields' in kwargs and 'deleted_actor' not in kwargs['update_fields']: if 'update_fields' in kwargs and 'deleted_actor' not in kwargs['update_fields']:
kwargs['update_fields'].append('deleted_actor') kwargs['update_fields'].append('deleted_actor')
hostname_char_limit = self._meta.get_field('action_node').max_length
self.action_node = settings.CLUSTER_HOST_ID[:hostname_char_limit]
super(ActivityStream, self).save(*args, **kwargs) super(ActivityStream, self).save(*args, **kwargs)

View File

@@ -19,14 +19,11 @@ 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.fields import JSONField
from awx.main.models.base import BaseModel, HasEditsMixin from awx.main.models.base import BaseModel, HasEditsMixin
from awx.main.models.inventory import InventoryUpdate
from awx.main.models.jobs import Job
from awx.main.models.projects import ProjectUpdate
from awx.main.models.unified_jobs import UnifiedJob from awx.main.models.unified_jobs import UnifiedJob
from awx.main.utils import get_cpu_capacity, get_mem_capacity, get_system_task_capacity from awx.main.utils import get_cpu_capacity, get_mem_capacity, get_system_task_capacity
from awx.main.models.mixins import RelatedJobsMixin from awx.main.models.mixins import RelatedJobsMixin
__all__ = ('Instance', 'InstanceGroup', 'JobOrigin', 'TowerScheduleState', 'TowerAnalyticsState') __all__ = ('Instance', 'InstanceGroup', 'TowerScheduleState', 'TowerAnalyticsState')
class HasPolicyEditsMixin(HasEditsMixin): class HasPolicyEditsMixin(HasEditsMixin):
@@ -266,24 +263,6 @@ class TowerAnalyticsState(SingletonModel):
last_run = models.DateTimeField(auto_now_add=True) last_run = models.DateTimeField(auto_now_add=True)
class JobOrigin(models.Model):
"""A model representing the relationship between a unified job and
the instance that was responsible for starting that job.
It may be possible that a job has no origin (the common reason for this
being that the job was started on Tower < 2.1 before origins were a thing).
This is fine, and code should be able to handle it. A job with no origin
is always assumed to *not* have the current instance as its origin.
"""
unified_job = models.OneToOneField(UnifiedJob, related_name='job_origin', on_delete=models.CASCADE)
instance = models.ForeignKey(Instance, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
app_label = 'main'
def schedule_policy_task(): def schedule_policy_task():
from awx.main.tasks import apply_cluster_membership_policies from awx.main.tasks import apply_cluster_membership_policies
connection.on_commit(lambda: apply_cluster_membership_policies.apply_async()) connection.on_commit(lambda: apply_cluster_membership_policies.apply_async())
@@ -311,31 +290,6 @@ def on_instance_deleted(sender, instance, using, **kwargs):
schedule_policy_task() schedule_policy_task()
# Unfortunately, the signal can't just be connected against UnifiedJob; it
# turns out that creating a model's subclass doesn't fire the signal for the
# superclass model.
@receiver(post_save, sender=InventoryUpdate)
@receiver(post_save, sender=Job)
@receiver(post_save, sender=ProjectUpdate)
def on_job_create(sender, instance, created=False, raw=False, **kwargs):
"""When a new job is created, save a record of its origin (the machine
that started the job).
"""
# Sanity check: We only want to create a JobOrigin record in cases where
# we are making a new record, and in normal situations.
#
# In other situations, we simply do nothing.
if raw or not created:
return
# Create the JobOrigin record, which attaches to the current instance
# (which started the job).
job_origin, new = JobOrigin.objects.get_or_create(
instance=Instance.objects.me(),
unified_job=instance,
)
class UnifiedJobTemplateInstanceGroupMembership(models.Model): class UnifiedJobTemplateInstanceGroupMembership(models.Model):
unifiedjobtemplate = models.ForeignKey( unifiedjobtemplate = models.ForeignKey(

View File

@@ -277,3 +277,22 @@ def test_saved_passwords_hidden_activity(workflow_job_template, job_template_wit
changes = json.loads(entry.changes) changes = json.loads(entry.changes)
assert 'survey_passwords' not in changes assert 'survey_passwords' not in changes
assert json.loads(changes['extra_data'])['bbbb'] == '$encrypted$' assert json.loads(changes['extra_data'])['bbbb'] == '$encrypted$'
@pytest.mark.django_db
def test_cluster_node_recorded(inventory, project):
jt = JobTemplate.objects.create(name='testjt', inventory=inventory, project=project)
with mock.patch('awx.main.models.activity_stream.settings.CLUSTER_HOST_ID', 'foo_host'):
job = jt.create_unified_job()
entry = ActivityStream.objects.filter(job=job).first()
assert entry.action_node == 'foo_host'
@pytest.mark.django_db
def test_cluster_node_long_node_name(inventory, project):
jt = JobTemplate.objects.create(name='testjt', inventory=inventory, project=project)
with mock.patch('awx.main.models.activity_stream.settings.CLUSTER_HOST_ID', 'f' * 700):
job = jt.create_unified_job()
# node name is very long, we just want to make sure it does not error
entry = ActivityStream.objects.filter(job=job).first()
assert entry.action_node.startswith('ffffff')