1
0
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:
Chris Meyers 2015-01-21 11:55:15 -05:00
commit 20d714cc00
5 changed files with 260 additions and 121 deletions

View 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'))

View File

@ -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))

View File

@ -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))

View File

@ -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)

View 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))