AC-537 Even more updates following database changes.

This commit is contained in:
Chris Church
2013-10-29 15:00:51 -04:00
parent 0c98b7ed69
commit 6815905882
6 changed files with 57 additions and 235 deletions

View File

@@ -118,6 +118,8 @@ class Migration(DataMigration):
team.name = team_name
team.save()
# FIXME: Check backwards migrations!!!
# Change credential password back to ssh_password.
for credential in orm.Credential.objects.all():
credential.ssh_username = credential.username

View File

@@ -29,6 +29,9 @@ class Migration(SchemaMigration):
# Deleting field 'Project.scm_username'
db.delete_column(u'main_project', 'scm_username')
# Deleting field 'InventorySource.source_tags'
db.delete_column(u'main_inventorysource', 'source_tags')
# Deleting field 'InventorySource.source_password'
db.delete_column(u'main_inventorysource', 'source_password')
@@ -70,6 +73,11 @@ class Migration(SchemaMigration):
self.gf('django.db.models.fields.CharField')(default='', max_length=256, null=True, blank=True),
keep_default=False)
# Adding field 'InventorySource.source_tags'
db.add_column(u'main_inventorysource', 'source_tags',
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True),
keep_default=False)
# Adding field 'InventorySource.source_password'
db.add_column(u'main_inventorysource', 'source_password',
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True),
@@ -231,7 +239,6 @@ class Migration(SchemaMigration):
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'source_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'none'", 'max_length': '32', 'null': 'True'}),
'update_interval': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),

View File

@@ -299,7 +299,7 @@ class CommonTask(PrimordialModel):
def start(self, **kwargs):
task_class = self._get_task_class()
needed = self._get_passwords_needed_to_start
needed = self._get_passwords_needed_to_start()
opts = dict([(field, kwargs.get(field, '')) for field in needed])
if not all(opts.values()):
return False
@@ -320,6 +320,7 @@ class CommonTask(PrimordialModel):
return bool(self.status in ('pending', 'waiting', 'running'))
def cancel(self):
# FIXME: Force cancel!
if self.can_cancel:
if not self.cancel_flag:
self.cancel_flag = True
@@ -822,27 +823,11 @@ class InventorySource(PrimordialModel):
default=None,
blank=True,
)
#source_username = models.CharField( # FIXME: Remove after migration
# max_length=1024,
# blank=True,
# default='',
#)
#source_password = models.CharField( # FIXME: Remove after migration
# max_length=1024,
# blank=True,
# default='',
#)
source_regions = models.CharField(
max_length=1024,
blank=True,
default='',
)
# FIXME: Remove tags field when making other migrations for credential changes!
source_tags = models.CharField(
max_length=1024,
blank=True,
default='',
)
overwrite = models.BooleanField(
default=False,
help_text=_('Overwrite local groups and hosts from remote inventory source.'),
@@ -894,21 +879,6 @@ class InventorySource(PrimordialModel):
# If update_fields has been specified, add our field names to it,
# if it hasn't been specified, then we're just doing a normal save.
update_fields = kwargs.get('update_fields', [])
# When first saving to the database, don't store any password field
# values, but instead save them until after the instance is created.
#if new_instance:
# for field in self.PASSWORD_FIELDS:
# value = getattr(self, field, '')
# setattr(self, '_saved_%s' % field, value)
# setattr(self, field, '')
# Otherwise, store encrypted values to the database.
#else:
# for field in self.PASSWORD_FIELDS:
# encrypted = encrypt_field(self, field, True)
# if getattr(self, field) != encrypted:
# setattr(self, field, encrypted)
# if field not in update_fields:
# update_fields.append(field)
# Update status and last_updated fields.
updated_fields = self.set_status_and_last_updated(save=False)
for field in updated_fields:
@@ -921,32 +891,9 @@ class InventorySource(PrimordialModel):
update_fields.append('inventory')
# Do the actual save.
super(InventorySource, self).save(*args, **kwargs)
# After saving a new instance for the first time (to get a primary
# key), set the password fields and save again.
#if new_instance:
# update_fields=[]
# for field in self.PASSWORD_FIELDS:
# saved_value = getattr(self, '_saved_%s' % field, '')
# if getattr(self, field) != saved_value:
# setattr(self, field, saved_value)
# update_fields.append(field)
# if update_fields:
# self.save(update_fields=update_fields)
source_vars_dict = VarsDictProperty('source_vars')
@property
def needs_source_password(self):
return self.source and self.source_password == 'ASK'
@property
def source_passwords_needed(self):
needed = []
for field in ('source_password',):
if getattr(self, 'needs_%s' % field):
needed.append(field)
return needed
def set_status_and_last_updated(self, save=True):
# Determine current status.
if self.source:
@@ -983,12 +930,8 @@ class InventorySource(PrimordialModel):
def update(self, **kwargs):
if self.can_update:
needed = self.source_passwords_needed
opts = dict([(field, kwargs.get(field, '')) for field in needed])
if not all(opts.values()):
return
inventory_update = self.inventory_updates.create()
inventory_update.start(**opts)
inventory_update.start()
return inventory_update
def get_absolute_url(self):
@@ -1032,9 +975,6 @@ class InventoryUpdate(CommonTask):
from awx.main.tasks import RunInventoryUpdate
return RunInventoryUpdate
def _get_passwords_needed_to_start(self):
return self.inventory_source.source_passwords_needed
class Credential(CommonModelNameNotUnique):
'''
@@ -1077,14 +1017,6 @@ class Credential(CommonModelNameNotUnique):
choices=KIND_CHOICES,
default='ssh',
)
#ssh_username = models.CharField(
# blank=True,
# default='',
# max_length=1024,
# verbose_name=_('SSH username'),
# help_text=_('SSH username for a job using this credential.'),
#)
username = models.CharField(
blank=True,
default='',
@@ -1092,13 +1024,6 @@ class Credential(CommonModelNameNotUnique):
verbose_name=_('Username'),
help_text=_('Username for this credential.'),
)
#ssh_password = models.CharField(
# blank=True,
# default='',
# max_length=1024,
# verbose_name=_('SSH password'),
# help_text=_('SSH password (or "ASK" to prompt the user).'),
#)
password = models.CharField(
blank=True,
default='',
@@ -1217,8 +1142,6 @@ class Project(CommonModel):
('successful', 'Successful'),
]
#PASSWORD_FIELDS = ('scm_password', 'scm_key_data', 'scm_key_unlock')
SCM_TYPE_CHOICES = [
('', _('Manual')),
('git', _('Git')),
@@ -1296,38 +1219,6 @@ class Project(CommonModel):
null=True,
default=None,
)
#scm_username = models.CharField(
# blank=True,
# null=True,
# default='',
# max_length=256,
# verbose_name=_('Username'),
# help_text=_('SCM username for this project.'),
#)
#scm_password = models.CharField(
# blank=True,
# null=True,
# default='',
# max_length=1024,
# verbose_name=_('Password'),
# help_text=_('SCM password (or "ASK" to prompt the user).'),
#)
#scm_key_data = models.TextField(
# blank=True,
# null=True,
# default='',
# verbose_name=_('SSH private key'),
# help_text=_('RSA or DSA private key to be used instead of password.'),
#)
#scm_key_unlock = models.CharField(
# max_length=1024,
# null=True,
# blank=True,
# default='',
# verbose_name=_('SSH key unlock'),
# help_text=_('Passphrase to unlock SSH private key if encrypted (or '
# '"ASK" to prompt the user).'),
#)
current_update = models.ForeignKey(
'ProjectUpdate',
null=True,
@@ -1364,21 +1255,6 @@ class Project(CommonModel):
# If update_fields has been specified, add our field names to it,
# if it hasn't been specified, then we're just doing a normal save.
update_fields = kwargs.get('update_fields', [])
# When first saving to the database, don't store any password field
# values, but instead save them until after the instance is created.
#if new_instance:
# for field in self.PASSWORD_FIELDS:
# value = getattr(self, field, '')
# setattr(self, '_saved_%s' % field, value)
# setattr(self, field, '')
# Otherwise, store encrypted values to the database.
#else:
# for field in self.PASSWORD_FIELDS:
# encrypted = encrypt_field(self, field, bool(field != 'scm_key_data'))
# if getattr(self, field) != encrypted:
# setattr(self, field, encrypted)
# if field not in update_fields:
# update_fields.append(field)
# Check if scm_type or scm_url changes.
if self.pk:
project_before = Project.objects.get(pk=self.pk)
@@ -1399,18 +1275,11 @@ class Project(CommonModel):
update_fields.append(field)
# Do the actual save.
super(Project, self).save(*args, **kwargs)
# After saving a new instance for the first time (to get a primary
# key), set the password fields and save again.
if new_instance:
update_fields=[]
# Generate local_path for SCM after initial save (so we have a PK).
if self.scm_type and not self.local_path.startswith('_'):
update_fields.append('local_path')
# for field in self.PASSWORD_FIELDS:
# saved_value = getattr(self, '_saved_%s' % field, '')
# if getattr(self, field) != saved_value:
# setattr(self, field, saved_value)
# update_fields.append(field)
if update_fields:
self.save(update_fields=update_fields)
# If we just created a new project with SCM and it doesn't require any
@@ -1420,14 +1289,11 @@ class Project(CommonModel):
@property
def needs_scm_password(self):
return self.scm_type and not self.scm_key_data and \
self.scm_password == 'ASK'
return self.credential and self.credential.needs_password
@property
def needs_scm_key_unlock(self):
return self.scm_type and self.scm_key_data and \
'ENCRYPTED' in decrypt_field(self, 'scm_key_data') and \
(not self.scm_key_unlock or self.scm_key_unlock == 'ASK')
return self.credential and self.credential.needs_ssh_key_unlock
@property
def scm_passwords_needed(self):
@@ -1690,11 +1556,6 @@ class JobTemplate(CommonModel):
'''
save_job = kwargs.pop('save', True)
kwargs['job_template'] = self
# Create new name with timestamp format to match jobs launched by the UI.
new_name = '%s %s' % (self.name, now().strftime('%Y-%m-%dT%H:%M:%S.%fZ'))
new_name = new_name[:-4] + 'Z'
kwargs.setdefault('name', new_name)
kwargs.setdefault('description', self.description)
kwargs.setdefault('job_type', self.job_type)
kwargs.setdefault('inventory', self.inventory)
kwargs.setdefault('project', self.project)

View File

@@ -287,10 +287,6 @@ class OrganizationSerializer(BaseSerializer):
class ProjectSerializer(BaseSerializer):
#scm_password = serializers.WritableField(required=False, default='')
#scm_key_data = serializers.WritableField(required=False, default='')
#scm_key_unlock = serializers.WritableField(required=False, default='')
playbooks = serializers.Field(source='playbooks', help_text='Array of playbooks available within this project.')
scm_delete_on_next_update = serializers.Field(source='scm_delete_on_next_update')
@@ -300,26 +296,8 @@ class ProjectSerializer(BaseSerializer):
'scm_branch', 'scm_clean',
'scm_delete_on_update', 'scm_delete_on_next_update',
'scm_update_on_launch', 'credential',
#'scm_username', 'scm_password', 'scm_key_data',
#'scm_key_unlock',
'last_update_failed', 'status', 'last_updated')
#def to_native(self, obj):
#ret = super(ProjectSerializer, self).to_native(obj)
# Replace the actual encrypted value with the string $encrypted$.
#for field in Project.PASSWORD_FIELDS:
# if field in ret and unicode(ret[field]).startswith('$encrypted$'):
# ret[field] = '$encrypted$'
#return ret
#def restore_object(self, attrs, instance=None):
# # If the value sent to the API startswith $encrypted$, ignore it.
# for field in Project.PASSWORD_FIELDS:
# if unicode(attrs.get(field, '')).startswith('$encrypted$'):
# attrs.pop(field, None)
# instance = super(ProjectSerializer, self).restore_object(attrs, instance)
# return instance
def get_related(self, obj):
if obj is None:
return {}
@@ -677,39 +655,11 @@ class InventorySourceSerializer(BaseSerializer):
model = InventorySource
fields = ('id', 'url', 'related', 'summary_fields', 'created',
'modified', 'inventory', 'group', 'source', 'source_path',
'source_vars', 'credential',
# 'source_username', 'source_password',
'source_regions', 'overwrite', 'overwrite_vars',
'update_on_launch', 'update_interval', 'last_update_failed',
'status', 'last_updated')
'source_vars', 'credential', 'source_regions', 'overwrite',
'overwrite_vars', 'update_on_launch', 'update_interval',
'last_update_failed', 'status', 'last_updated')
read_only_fields = ('inventory', 'group')
def to_native(self, obj):
ret = super(InventorySourceSerializer, self).to_native(obj)
# Replace the actual encrypted value with the string $encrypted$.
#for field in InventorySource.PASSWORD_FIELDS:
# if field in ret and unicode(ret[field]).startswith('$encrypted$'):
# ret[field] = '$encrypted$'
# Make regions/tags into a list of strings.
#for field in ('source_regions', 'source_tags'):
# if field in ret:
# value = ret[field]
# if isinstance(value, basestring):
# ret[field] = [x.strip() for x in value.split(',') if x.strip()]
return ret
def restore_object(self, attrs, instance=None):
# If the value sent to the API startswith $encrypted$, ignore it.
#for field in InventorySource.PASSWORD_FIELDS:
# if unicode(attrs.get(field, '')).startswith('$encrypted$'):
# attrs.pop(field, None)
#for field in ('source_regions', 'source_tags'):
# value = attrs.get(field, [])
# if isinstance(value, (list,tuple)):
# attrs[field] = ','.join([unicode(x).strip() for x in value])
instance = super(InventorySourceSerializer, self).restore_object(attrs, instance)
return instance
def get_related(self, obj):
if obj is None:
return {}
@@ -855,21 +805,21 @@ class CredentialSerializer(BaseSerializer):
'ssh_key_unlock', 'sudo_username',
'sudo_password', 'user', 'team',)
#def to_native(self, obj):
# ret = super(CredentialSerializer, self).to_native(obj)
# # Replace the actual encrypted value with the string $encrypted$.
# for field in Credential.PASSWORD_FIELDS:
# if field in ret and unicode(ret[field]).startswith('$encrypted$'):
# ret[field] = '$encrypted$'
# return ret
def to_native(self, obj):
ret = super(CredentialSerializer, self).to_native(obj)
# Replace the actual encrypted value with the string $encrypted$.
for field in Credential.PASSWORD_FIELDS:
if field in ret and unicode(ret[field]).startswith('$encrypted$'):
ret[field] = '$encrypted$'
return ret
#def restore_object(self, attrs, instance=None):
# # If the value sent to the API startswith $encrypted$, ignore it.
# for field in Credential.PASSWORD_FIELDS:
# if unicode(attrs.get(field, '')).startswith('$encrypted$'):
# attrs.pop(field, None)
# instance = super(CredentialSerializer, self).restore_object(attrs, instance)
# return instance
def restore_object(self, attrs, instance=None):
# If the value sent to the API startswith $encrypted$, ignore it.
for field in Credential.PASSWORD_FIELDS:
if unicode(attrs.get(field, '')).startswith('$encrypted$'):
attrs.pop(field, None)
instance = super(CredentialSerializer, self).restore_object(attrs, instance)
return instance
def get_related(self, obj):
if obj is None:

View File

@@ -301,7 +301,7 @@ class RunJob(BaseTask):
passwords = super(RunJob, self).build_passwords(job, **kwargs)
creds = job.credential
if creds:
for field in ('ssh_key_unlock', 'ssh_password', 'sudo_password'):
for field in ('ssh_key_unlock', 'password', 'sudo_password'):
value = kwargs.get(field, decrypt_field(creds, field))
if value not in ('', 'ASK'):
passwords[field] = value
@@ -340,7 +340,7 @@ class RunJob(BaseTask):
creds = job.credential
ssh_username, sudo_username = '', ''
if creds:
ssh_username = kwargs.get('ssh_username', creds.ssh_username)
ssh_username = kwargs.get('username', creds.username)
sudo_username = kwargs.get('sudo_username', creds.sudo_username)
# Always specify the normal SSH user as root by default. Since this
# task is normally running in the background under a service account,
@@ -392,8 +392,8 @@ class RunJob(BaseTask):
d = super(RunJob, self).get_password_prompts()
d.update({
r'sudo password.*:': 'sudo_password',
r'SSH password:': 'ssh_password',
r'Password:': 'ssh_password',
r'SSH password:': 'password',
r'Password:': 'password',
})
return d
@@ -509,7 +509,8 @@ class RunProjectUpdate(BaseTask):
Return SSH private key data needed for this project update.
'''
project = project_update.project
return decrypt_field(project, 'scm_key_data') or None
if project.credential:
return decrypt_field(project.credential, 'ssh_key_data') or None
def build_passwords(self, project_update, **kwargs):
'''
@@ -519,12 +520,13 @@ class RunProjectUpdate(BaseTask):
passwords = super(RunProjectUpdate, self).build_passwords(project_update,
**kwargs)
project = project_update.project
value = kwargs.get('scm_key_unlock', decrypt_field(project, 'scm_key_unlock'))
if value not in ('', 'ASK'):
passwords['ssh_key_unlock'] = value
passwords['scm_username'] = project.scm_username
passwords['scm_password'] = kwargs.get('scm_password',
decrypt_field(project, 'scm_password'))
if project.credential:
value = kwargs.get('scm_key_unlock', decrypt_field(project.credential, 'ssh_key_unlock'))
if value not in ('', 'ASK'):
passwords['scm_key_unlock'] = value
passwords['scm_username'] = project.scm_username
passwords['scm_password'] = kwargs.get('scm_password',
decrypt_field(project.credential, 'password'))
return passwords
def build_env(self, project_update, **kwargs):
@@ -547,9 +549,9 @@ class RunProjectUpdate(BaseTask):
scm_type = project.scm_type
scm_url = update_scm_url(scm_type, project.scm_url)
scm_url_parts = urlparse.urlsplit(scm_url)
scm_username = kwargs.get('passwords', {}).get('scm_username', '')
scm_username = kwargs.get('passwords', {}).get('username', '')
scm_username = scm_username or scm_url_parts.username or ''
scm_password = kwargs.get('passwords', {}).get('scm_password', '')
scm_password = kwargs.get('passwords', {}).get('password', '')
scm_password = scm_password or scm_url_parts.password or ''
if scm_username and scm_password not in ('ASK', ''):
if scm_type == 'svn':
@@ -731,9 +733,11 @@ class RunInventoryUpdate(BaseTask):
elif inventory_source.source == 'rackspace':
section = 'rackspace_cloud'
cp.add_section(section)
cp.set(section, 'username', inventory_source.source_username)
cp.set(section, 'api_key', decrypt_field(inventory_source,
'source_password'))
credential = inventory_source.credential
if credential:
cp.set(section, 'username', credential.username)
cp.set(section, 'api_key', decrypt_field(credential,
'password'))
# Return INI content.
if cp.sections():
f = cStringIO.StringIO()
@@ -747,9 +751,10 @@ class RunInventoryUpdate(BaseTask):
passwords = super(RunInventoryUpdate, self).build_passwords(inventory_update,
**kwargs)
inventory_source = inventory_update.inventory_source
passwords['source_username'] = inventory_source.source_username
passwords['source_password'] = kwargs.get('source_password', \
decrypt_field(inventory_source, 'source_password'))
credential = inventory_source.credential
if credential:
passwords['source_username'] = credential.username
passwords['source_password'] = decrypt_field(credential, 'password'))
return passwords
def build_env(self, inventory_update, **kwargs):

View File

@@ -748,8 +748,6 @@ class InventorySourceUpdateView(GenericAPIView):
data = dict(
can_update=obj.can_update,
)
if obj.source:
data['passwords_needed_to_update'] = obj.source_passwords_needed
return Response(data)
def post(self, request, *args, **kwargs):
@@ -757,8 +755,7 @@ class InventorySourceUpdateView(GenericAPIView):
if obj.can_update:
inventory_update = obj.update(**request.DATA)
if not inventory_update:
data = dict(passwords_needed_to_update=obj.source_passwords_needed)
return Response(data, status=status.HTTP_400_BAD_REQUEST)
return Response({}, status=status.HTTP_400_BAD_REQUEST)
else:
headers = {'Location': inventory_update.get_absolute_url()}
return Response(status=status.HTTP_202_ACCEPTED, headers=headers)