From 55f2125a51b2b9b482ca57bf4379fae7498a44d5 Mon Sep 17 00:00:00 2001 From: Rebeccah Date: Mon, 13 Sep 2021 12:16:21 -0400 Subject: [PATCH 1/2] if the user provides a uuid and it exists, allow that to tie to the instance, which allows the user to update the instance based on the UUID (includeding updating the hostname) should they choose to do so. --- awx/main/managers.py | 18 +++++++++++++++--- awx/main/models/ha.py | 4 ++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/awx/main/managers.py b/awx/main/managers.py index b91bda42bd..9f62064a76 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -12,9 +12,10 @@ from awx.main.utils.pglock import advisory_lock from awx.main.utils.common import get_capacity_type from awx.main.constants import RECEPTOR_PENDING -___all__ = ['HostManager', 'InstanceManager', 'InstanceGroupManager', 'DeferJobCreatedManager'] +___all__ = ['HostManager', 'InstanceManager', 'InstanceGroupManager', 'DeferJobCreatedManager', 'UUID_DEFAULT'] logger = logging.getLogger('awx.main.managers') +UUID_DEFAULT = '00000000-0000-0000-0000-000000000000' class DeferJobCreatedManager(models.Manager): @@ -105,7 +106,7 @@ class InstanceManager(models.Manager): """Return the currently active instance.""" # If we are running unit tests, return a stub record. if settings.IS_TESTING(sys.argv) or hasattr(sys, '_called_from_test'): - return self.model(id=1, hostname=settings.CLUSTER_HOST_ID, uuid='00000000-0000-0000-0000-000000000000') + return self.model(id=1, hostname=settings.CLUSTER_HOST_ID, uuid=UUID_DEFAULT) node = self.filter(hostname=settings.CLUSTER_HOST_ID) if node.exists(): @@ -115,6 +116,7 @@ class InstanceManager(models.Manager): def register(self, uuid=None, hostname=None, ip_address=None, node_type='hybrid', defaults=None): if not hostname: hostname = settings.CLUSTER_HOST_ID + with advisory_lock('instance_registration_%s' % hostname): if settings.AWX_AUTO_DEPROVISION_INSTANCES: # detect any instances with the same IP address. @@ -127,11 +129,21 @@ class InstanceManager(models.Manager): other_inst.save(update_fields=['ip_address']) logger.warning("IP address {0} conflict detected, ip address unset for host {1}.".format(ip_address, other_hostname)) - # Return existing instance that matches hostname + # get the instance based on the hostname instance = self.filter(hostname=hostname) + + # Check or update the existing uuid + if uuid is not None and uuid != UUID_DEFAULT: + if self.filter(uuid=uuid).exists(): + instance = self.filter(uuid=uuid) + + # Return existing instance if instance.exists(): instance = instance.get() update_fields = [] + if instance.hostname != hostname: + instance.hostname = hostname + update_fields.append('hostname') if instance.ip_address != ip_address: instance.ip_address = ip_address update_fields.append('ip_address') diff --git a/awx/main/models/ha.py b/awx/main/models/ha.py index 0b75e4a964..d2686708e5 100644 --- a/awx/main/models/ha.py +++ b/awx/main/models/ha.py @@ -18,7 +18,7 @@ from solo.models import SingletonModel from awx import __version__ as awx_application_version from awx.api.versioning import reverse -from awx.main.managers import InstanceManager, InstanceGroupManager +from awx.main.managers import InstanceManager, InstanceGroupManager, UUID_DEFAULT from awx.main.fields import JSONField from awx.main.models.base import BaseModel, HasEditsMixin, prevent_search from awx.main.models.unified_jobs import UnifiedJob @@ -59,7 +59,7 @@ class Instance(HasPolicyEditsMixin, BaseModel): objects = InstanceManager() # Fields set in instance registration - uuid = models.CharField(max_length=40, default='00000000-0000-0000-0000-000000000000') + uuid = models.CharField(max_length=40, default=UUID_DEFAULT) hostname = models.CharField(max_length=250, unique=True) ip_address = models.CharField( blank=True, From a9f4011a4503ae4317e64ab59b6ad0381a13ce0c Mon Sep 17 00:00:00 2001 From: Rebeccah Date: Fri, 17 Sep 2021 15:39:43 -0400 Subject: [PATCH 2/2] defensive code for getting instance added, also simplified nested if statements, rewrote some comments add a logger warning that the instance is being grabbed by the hostname and not the UUID --- awx/main/managers.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/awx/main/managers.py b/awx/main/managers.py index 9f62064a76..7b9164ef32 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -129,24 +129,25 @@ class InstanceManager(models.Manager): other_inst.save(update_fields=['ip_address']) logger.warning("IP address {0} conflict detected, ip address unset for host {1}.".format(ip_address, other_hostname)) - # get the instance based on the hostname - instance = self.filter(hostname=hostname) - - # Check or update the existing uuid - if uuid is not None and uuid != UUID_DEFAULT: - if self.filter(uuid=uuid).exists(): - instance = self.filter(uuid=uuid) + # Return existing instance that matches hostname or UUID (default to UUID) + if uuid is not None and uuid != UUID_DEFAULT and self.filter(uuid=uuid).exists(): + instance = self.filter(uuid=uuid) + else: + # if instance was not retrieved by uuid and hostname was, use the hostname + instance = self.filter(hostname=hostname) # Return existing instance if instance.exists(): - instance = instance.get() + instance = instance.first() # in the unusual occasion that there is more than one, only get one update_fields = [] + # if instance was retrieved by uuid and hostname has changed, update hostname if instance.hostname != hostname: + logger.warning("passed in hostname {0} is different from the original hostname {1}, updating to {0}".format(hostname, instance.hostname)) instance.hostname = hostname update_fields.append('hostname') + # if any other fields are to be updated if instance.ip_address != ip_address: instance.ip_address = ip_address - update_fields.append('ip_address') if instance.node_type != node_type: instance.node_type = node_type update_fields.append('node_type')