mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 08:21:15 +03:00
Merge pull request #30 from chrismeyersfsu/improvement-tower-manage_ha
unified tower-manage *_instance commands
This commit is contained in:
commit
20d714cc00
139
awx/main/management/commands/_base_instance.py
Normal file
139
awx/main/management/commands/_base_instance.py
Normal file
@ -0,0 +1,139 @@
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from optparse import make_option
|
||||
from django.conf import settings
|
||||
|
||||
class OptionEnforceError(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class BaseCommandInstance(BaseCommand):
|
||||
#option_list = BaseCommand.option_list
|
||||
|
||||
def __init__(self):
|
||||
super(BaseCommandInstance, self).__init__()
|
||||
self.enforce_roles = False
|
||||
self.enforce_hostname_set = False
|
||||
self.enforce_unique_find = False
|
||||
|
||||
self.option_primary = False
|
||||
self.option_secondary = False
|
||||
self.option_hostname = None
|
||||
self.option_uuid = None
|
||||
|
||||
self.UUID = settings.SYSTEM_UUID
|
||||
self.unique_fields = {}
|
||||
|
||||
@staticmethod
|
||||
def generate_option_hostname():
|
||||
return make_option('--hostname',
|
||||
dest='hostname',
|
||||
default='',
|
||||
help='Find instance by specified hostname.')
|
||||
|
||||
@staticmethod
|
||||
def generate_option_hostname_set():
|
||||
return make_option('--hostname',
|
||||
dest='hostname',
|
||||
default='',
|
||||
help='Hostname to assign to the new instance.')
|
||||
|
||||
@staticmethod
|
||||
def generate_option_primary():
|
||||
return make_option('--primary',
|
||||
action='store_true',
|
||||
default=False,
|
||||
dest='primary',
|
||||
help='Register instance as primary.')
|
||||
|
||||
@staticmethod
|
||||
def generate_option_secondary():
|
||||
return make_option('--secondary',
|
||||
action='store_true',
|
||||
default=False,
|
||||
dest='secondary',
|
||||
help='Register instance as secondary.')
|
||||
|
||||
@staticmethod
|
||||
def generate_option_uuid():
|
||||
return make_option('--uuid',
|
||||
dest='uuid',
|
||||
default='',
|
||||
help='Find instance by specified uuid.')
|
||||
|
||||
def include_options_roles(self):
|
||||
BaseCommand.option_list += ( BaseCommandInstance.generate_option_primary(), BaseCommandInstance.generate_option_secondary(), )
|
||||
self.enforce_roles = True
|
||||
|
||||
def include_option_hostname_set(self):
|
||||
BaseCommand.option_list += ( BaseCommandInstance.generate_option_hostname_set(), )
|
||||
self.enforce_hostname_set = True
|
||||
|
||||
def include_option_hostname_uuid_find(self):
|
||||
BaseCommand.option_list += ( BaseCommandInstance.generate_option_hostname(), BaseCommandInstance.generate_option_uuid(), )
|
||||
self.enforce_unique_find = True
|
||||
|
||||
def get_option_hostname(self):
|
||||
return self.option_hostname
|
||||
def get_option_uuid(self):
|
||||
return self.option_uuid
|
||||
def is_option_primary(self):
|
||||
return self.option_primary
|
||||
def is_option_secondary(self):
|
||||
return self.option_secondary
|
||||
def get_UUID(self):
|
||||
return self.UUID
|
||||
# for the enforce_unique_find policy
|
||||
def get_unique_fields(self):
|
||||
return self.unique_fields
|
||||
|
||||
@property
|
||||
def usage_error(self):
|
||||
if self.enforce_roles and self.enforce_hostname_set:
|
||||
return CommandError('--hostname and one of --primary or --secondary is required.')
|
||||
elif self.enforce_hostname_set:
|
||||
return CommandError('--hostname is required.')
|
||||
elif self.enforce_roles:
|
||||
return CommandError('One of --primary or --secondary is required.')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if self.enforce_hostname_set and self.enforce_unique_find:
|
||||
raise OptionEnforceError('Can not enforce --hostname as a setter and --hostname as a getter')
|
||||
|
||||
if self.enforce_roles:
|
||||
self.option_primary = options['primary']
|
||||
self.option_secondary = options['secondary']
|
||||
|
||||
if self.is_option_primary() and self.is_option_secondary() or not (self.is_option_primary() or self.is_option_secondary()):
|
||||
raise self.usage_error
|
||||
|
||||
if self.enforce_hostname_set:
|
||||
if options['hostname']:
|
||||
self.option_hostname = options['hostname']
|
||||
else:
|
||||
raise self.usage_error
|
||||
|
||||
if self.enforce_unique_find:
|
||||
if options['hostname']:
|
||||
self.unique_fields['hostname'] = self.option_hostname = options['hostname']
|
||||
|
||||
if options['uuid']:
|
||||
self.unique_fields['uuid'] = self.option_uuid = options['uuid']
|
||||
|
||||
if len(self.unique_fields) == 0:
|
||||
self.unique_fields['uuid'] = self.get_UUID()
|
||||
|
||||
@staticmethod
|
||||
def __instance_str(instance, fields):
|
||||
string = '('
|
||||
for field in fields:
|
||||
string += '%s="%s",' % (field, getattr(instance, field))
|
||||
if len(fields) > 0:
|
||||
string = string[:-1]
|
||||
string += ')'
|
||||
return string
|
||||
|
||||
@staticmethod
|
||||
def instance_str(instance):
|
||||
return BaseCommandInstance.__instance_str(instance, ('uuid', 'hostname', 'role'))
|
@ -5,15 +5,18 @@ from optparse import make_option
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from awx.main.management.commands._base_instance import BaseCommandInstance
|
||||
instance_str = BaseCommandInstance.instance_str
|
||||
|
||||
from awx.main.models import Instance
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
class Command(BaseCommandInstance):
|
||||
"""List instances from the Tower database
|
||||
"""
|
||||
|
||||
def handle(self, **options):
|
||||
super(Command, self).__init__()
|
||||
|
||||
for instance in Instance.objects.all():
|
||||
print("uuid: %s; hostname: %s; primary: %s; created: %s; modified: %s" %
|
||||
(instance.uuid, instance.hostname, instance.primary, instance.created, instance.modified))
|
||||
|
@ -1,114 +1,61 @@
|
||||
# Copyright (c) 2014 Ansible, Inc.
|
||||
# All Rights Reserved
|
||||
|
||||
from optparse import make_option
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from awx.main.management.commands._base_instance import BaseCommandInstance
|
||||
instance_str = BaseCommandInstance.instance_str
|
||||
|
||||
from awx.main.models import Instance
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Regsiter this instance with the database for HA tracking.
|
||||
class Command(BaseCommandInstance):
|
||||
"""Internal tower command.
|
||||
Regsiter this instance with the database for HA tracking.
|
||||
|
||||
This command is idempotent. It will register the machine if it is not
|
||||
yet present and do nothing if the machine is already registered.
|
||||
This command is idempotent.
|
||||
|
||||
If a second primary machine is registered, the first primary machine will
|
||||
become a secondary machine, and the newly registered primary machine
|
||||
will be primary (unless `--timid` is provided, in which case the command
|
||||
will simply error out).
|
||||
|
||||
This command will also error out under the following circumstances:
|
||||
This command will error out in the following conditions:
|
||||
|
||||
* Attempting to register a secondary machine with no primary machines.
|
||||
* Attempting to register this machine with a different state than its
|
||||
existing registration plus `--timid`.
|
||||
* Attempting to register a primary instance when a different primary
|
||||
instance exists.
|
||||
* Attempting to re-register an instance with changed values.
|
||||
"""
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('--timid',
|
||||
action='store_true',
|
||||
dest='timid',
|
||||
help='Fail if the primary host specified is already registered as '
|
||||
'another instance or there already exists a primary different '
|
||||
'from the one specified.'),
|
||||
make_option('--hostname',
|
||||
dest='hostname',
|
||||
default='',
|
||||
help='instance to register'),
|
||||
make_option('--primary',
|
||||
action='store_true',
|
||||
dest='primary',
|
||||
help='register instance as primary'),
|
||||
make_option('--secondary',
|
||||
action='store_false',
|
||||
dest='primary',
|
||||
help='register instance as secondary'),
|
||||
)
|
||||
def __init__(self):
|
||||
super(Command, self).__init__()
|
||||
|
||||
def handle(self, **options):
|
||||
uuid = settings.SYSTEM_UUID
|
||||
timid = options['timid']
|
||||
self.include_options_roles()
|
||||
self.include_option_hostname_set()
|
||||
|
||||
# Is there an existing record for this machine?
|
||||
# If so, retrieve that record and look for issues.
|
||||
def handle(self, *args, **options):
|
||||
super(Command, self).handle(*args, **options)
|
||||
|
||||
uuid = self.get_UUID()
|
||||
|
||||
# Is there an existing record for this machine? If so, retrieve that record and look for issues.
|
||||
try:
|
||||
instance = Instance.objects.get(uuid=uuid)
|
||||
existing = True
|
||||
if instance.hostname != self.get_option_hostname():
|
||||
raise CommandError('Instance already registered with a different hostname %s.' % instance_str(instance))
|
||||
print("Instance already registered %s" % instance_str(instance))
|
||||
except Instance.DoesNotExist:
|
||||
instance = Instance(uuid=uuid)
|
||||
existing = False
|
||||
# Get a status on primary machines (excluding this one, regardless of its status).
|
||||
other_instances = Instance.objects.exclude(uuid=uuid)
|
||||
primaries = other_instances.filter(primary=True).count()
|
||||
|
||||
# Get a status on primary machines (excluding this one, regardless
|
||||
# of its status).
|
||||
other_instances = Instance.objects.exclude(uuid=uuid)
|
||||
primaries = other_instances.filter(primary=True).count()
|
||||
# If this instance is being set to primary and a *different* primary machine alreadyexists, error out.
|
||||
if self.is_option_primary() and primaries:
|
||||
raise CommandError('Another instance is already registered as primary.')
|
||||
|
||||
# Sanity check: If we're supposed to be being timid, then ensure
|
||||
# that there's no crazy mosh pits happening here.
|
||||
#
|
||||
# If the primacy setting doesn't match what is already on
|
||||
# the instance, error out.
|
||||
if existing and timid and instance.primary != options['primary']:
|
||||
raise CommandError('This instance is already registered as a '
|
||||
'%s instance.' % instance.role)
|
||||
# Lastly, if there are no primary machines at all, then don't allow this to be registered as a secondary machine.
|
||||
if self.is_option_secondary() and not primaries:
|
||||
raise CommandError('Unable to register a secondary machine until another primary machine has been registered.')
|
||||
|
||||
# If this instance is being set to primary and a *different* primary
|
||||
# machine alreadyexists, error out if we're supposed to be timid.
|
||||
if timid and options['primary'] and primaries:
|
||||
raise CommandError('Another instance is already registered as '
|
||||
'primary.')
|
||||
# Okay, we've checked for appropriate errata; perform the registration.
|
||||
instance = Instance(uuid=uuid, primary=self.is_option_primary(), hostname=self.get_option_hostname())
|
||||
instance.save()
|
||||
|
||||
# Lastly, if there are no primary machines at all, then don't allow
|
||||
# this to be registered as a secondary machine.
|
||||
if not options['primary'] and not primaries:
|
||||
raise CommandError('Unable to register a secondary machine until '
|
||||
'another primary machine has been registered.')
|
||||
|
||||
# Sanity check: An IP address is required if this is an initial
|
||||
# registration.
|
||||
if not existing and not options['hostname']:
|
||||
raise CommandError('An explicit hostname is required at initial '
|
||||
'registration.')
|
||||
|
||||
# If this is a primary machine and there is another primary machine,
|
||||
# it must be de-primary-ified.
|
||||
if options['primary'] and primaries:
|
||||
for old_primary in other_instances.filter(primary=True):
|
||||
old_primary.primary = False
|
||||
old_primary.save()
|
||||
|
||||
# Okay, we've checked for appropriate errata; perform the registration.
|
||||
dirty = any([
|
||||
instance.primary is not options['primary'],
|
||||
options['hostname'] and
|
||||
instance.hostname != options['hostname'],
|
||||
])
|
||||
instance.primary = options['primary']
|
||||
if options['hostname']:
|
||||
instance.hostname = options['hostname']
|
||||
instance.save()
|
||||
|
||||
# Done!
|
||||
print('Instance %s registered (changed: %r).' % (uuid, dirty))
|
||||
# Done!
|
||||
print('Successfully registered instance %s.' % instance_str(instance))
|
||||
|
@ -5,50 +5,40 @@ from optparse import make_option
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from awx.main.management.commands._base_instance import BaseCommandInstance
|
||||
instance_str = BaseCommandInstance.instance_str
|
||||
|
||||
from awx.main.models import Instance
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Remove an existing instance from the HA instance table.
|
||||
class Command(BaseCommandInstance):
|
||||
"""Internal tower command.
|
||||
Remove an existing instance from the HA instance table.
|
||||
|
||||
This command is idempotent. It will remove the machine if it is
|
||||
present and do nothing if the machine is absent.
|
||||
This command is idempotent.
|
||||
|
||||
This command will cowardly refuse to remove the primary machine.
|
||||
This command will error out in the following conditions:
|
||||
|
||||
* Attempting to remove a primary instance.
|
||||
"""
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('--hostname', dest='hostname', default=''),
|
||||
make_option('--uuid', dest='uuid', default=''),
|
||||
)
|
||||
def __init__(self):
|
||||
super(Command, self).__init__()
|
||||
|
||||
def handle(self, **options):
|
||||
# Remove any empty options from the options dictionary.
|
||||
fields = {}
|
||||
for field in ('uuid', 'hostname'):
|
||||
if options[field]:
|
||||
fields[field] = options[field]
|
||||
self.include_option_hostname_uuid_find()
|
||||
|
||||
# At least one of hostname or uuid must be set.
|
||||
if not fields:
|
||||
raise CommandError('You must provide either --uuid or --hostname.')
|
||||
|
||||
# Is there an existing record for this machine?
|
||||
# If so, retrieve that record and look for issues.
|
||||
def handle(self, *args, **options):
|
||||
# Is there an existing record for this machine? If so, retrieve that record and look for issues.
|
||||
try:
|
||||
# Get the instance.
|
||||
instance = Instance.objects.get(**fields)
|
||||
instance = Instance.objects.get(**self.get_unique_fields())
|
||||
|
||||
# Sanity check: Do not remove the primary instance.
|
||||
if instance.primary:
|
||||
raise CommandError('I cowardly refuse to remove the primary '
|
||||
'instance.')
|
||||
raise CommandError('I cowardly refuse to remove the primary instance %s.' % instance_str(instance))
|
||||
|
||||
# Remove the instance.
|
||||
instance.delete()
|
||||
dirty = True
|
||||
print('Successfully removed instance %s.' % instance_str(instance))
|
||||
except Instance.DoesNotExist:
|
||||
dirty = False
|
||||
print('No matching instance found to remove.')
|
||||
|
||||
# Done!
|
||||
print('Instance removed (changed: %r).' % dirty)
|
||||
|
60
awx/main/management/commands/update_instance.py
Normal file
60
awx/main/management/commands/update_instance.py
Normal file
@ -0,0 +1,60 @@
|
||||
# Copyright (c) 2014 Ansible, Inc.
|
||||
# All Rights Reserved
|
||||
|
||||
from optparse import make_option
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import transaction
|
||||
|
||||
from awx.main.management.commands._base_instance import BaseCommandInstance
|
||||
instance_str = BaseCommandInstance.instance_str
|
||||
|
||||
from awx.main.models import Instance
|
||||
|
||||
class Command(BaseCommandInstance):
|
||||
"""Set an already registered instance to primary or secondary for HA
|
||||
tracking.
|
||||
|
||||
This command is idempotent. Settings a new primary instance when a
|
||||
primary instance already exists will result in the existing primary
|
||||
instance set to secondary and the new primary set to primary.
|
||||
|
||||
This command will error out under the following circumstances:
|
||||
|
||||
* Attempting to update a secondary instance with no primary instances.
|
||||
* When a matching instance is not found.
|
||||
"""
|
||||
def __init__(self):
|
||||
super(Command, self).__init__()
|
||||
|
||||
self.include_options_roles()
|
||||
self.include_option_hostname_uuid_find()
|
||||
|
||||
@transaction.atomic
|
||||
def handle(self, *args, **options):
|
||||
super(Command, self).handle(*args, **options)
|
||||
|
||||
# Is there an existing record for this machine? If so, retrieve that record and look for issues.
|
||||
try:
|
||||
instance = Instance.objects.get(**self.get_unique_fields())
|
||||
existing = True
|
||||
except Instance.DoesNotExist:
|
||||
raise CommandError('No matching instance found to update.')
|
||||
|
||||
# Get a status on primary machines (excluding this one, regardless of its status).
|
||||
other_instances = Instance.objects.exclude(**self.get_unique_fields())
|
||||
primaries = other_instances.filter(primary=True).count()
|
||||
|
||||
# If this is a primary machine and there is another primary machine, it must be de-primary-ified.
|
||||
if self.is_option_primary() and primaries:
|
||||
for old_primary in other_instances.filter(primary=True):
|
||||
old_primary.primary = False
|
||||
old_primary.save()
|
||||
|
||||
# Okay, we've checked for appropriate errata; perform the registration.
|
||||
instance.primary = self.is_option_primary()
|
||||
instance.save()
|
||||
|
||||
# Done!
|
||||
print('Successfully updated instance role %s' % instance_str(instance))
|
Loading…
Reference in New Issue
Block a user