mirror of
https://github.com/ansible/awx.git
synced 2026-04-14 06:29:25 -02:30
Beginning PEM file support for GCE.
This commit is contained in:
@@ -966,6 +966,7 @@ class InventorySourceOptionsSerializer(BaseSerializer):
|
|||||||
field_opts = metadata.get('source_regions', {})
|
field_opts = metadata.get('source_regions', {})
|
||||||
field_opts['ec2_region_choices'] = self.opts.model.get_ec2_region_choices()
|
field_opts['ec2_region_choices'] = self.opts.model.get_ec2_region_choices()
|
||||||
field_opts['rax_region_choices'] = self.opts.model.get_rax_region_choices()
|
field_opts['rax_region_choices'] = self.opts.model.get_rax_region_choices()
|
||||||
|
field_opts['gce_region_choices'] = self.opts.model.get_gce_region_choices()
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
def to_native(self, obj):
|
def to_native(self, obj):
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
# Django
|
# Django
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields.related import SingleRelatedObjectDescriptor
|
from django.db.models.fields.related import SingleRelatedObjectDescriptor
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
# South
|
# South
|
||||||
from south.modelsinspector import add_introspection_rules
|
from south.modelsinspector import add_introspection_rules
|
||||||
@@ -38,3 +40,57 @@ class AutoOneToOneField(models.OneToOneField):
|
|||||||
|
|
||||||
add_introspection_rules([([AutoOneToOneField], [], {})],
|
add_introspection_rules([([AutoOneToOneField], [], {})],
|
||||||
[r'^awx\.main\.fields\.AutoOneToOneField'])
|
[r'^awx\.main\.fields\.AutoOneToOneField'])
|
||||||
|
|
||||||
|
|
||||||
|
# Copied, flat out, from Django 1.6.
|
||||||
|
# Vendored here because Tower is run against Django 1.5.
|
||||||
|
#
|
||||||
|
# Original:
|
||||||
|
# github.com/django/django/blob/master/django/db/models/fields/__init__.py
|
||||||
|
#
|
||||||
|
# Django is:
|
||||||
|
# Copyright (c) Django Software Foundation and individual contributors.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Used under license:
|
||||||
|
# github.com/django/django/blob/master/LICENSE
|
||||||
|
class BinaryField(models.Field):
|
||||||
|
description = _("Raw binary data")
|
||||||
|
empty_values = [None, b'']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs['editable'] = False
|
||||||
|
super(BinaryField, self).__init__(*args, **kwargs)
|
||||||
|
if self.max_length is not None:
|
||||||
|
self.validators.append(validators.MaxLengthValidator(self.max_length))
|
||||||
|
|
||||||
|
def get_internal_type(self):
|
||||||
|
return "BinaryField"
|
||||||
|
|
||||||
|
def get_default(self):
|
||||||
|
if self.has_default() and not callable(self.default):
|
||||||
|
return self.default
|
||||||
|
default = super(BinaryField, self).get_default()
|
||||||
|
if default == '':
|
||||||
|
return b''
|
||||||
|
return default
|
||||||
|
|
||||||
|
def get_db_prep_value(self, value, connection, prepared=False):
|
||||||
|
value = super(BinaryField, self).get_db_prep_value(value, connection, prepared)
|
||||||
|
if value is not None:
|
||||||
|
return connection.Database.Binary(value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def value_to_string(self, obj):
|
||||||
|
"""Binary data is serialized as base64"""
|
||||||
|
return b64encode(force_bytes(self._get_val_from_obj(obj))).decode('ascii')
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
# If it's a string, it should be base64-encoded data
|
||||||
|
if isinstance(value, six.text_type):
|
||||||
|
return six.memoryview(b64decode(force_bytes(value)))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
add_introspection_rules([([BinaryField], [], {})],
|
||||||
|
[r'^awx\.main\.fields\.BinaryField'])
|
||||||
|
|||||||
@@ -13,12 +13,27 @@ from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
|
from awx.main import storage
|
||||||
|
from awx.main.fields import BinaryField
|
||||||
from awx.main.utils import decrypt_field
|
from awx.main.utils import decrypt_field
|
||||||
from awx.main.models.base import *
|
from awx.main.models.base import *
|
||||||
|
|
||||||
__all__ = ['Credential']
|
__all__ = ['Credential']
|
||||||
|
|
||||||
|
|
||||||
|
class PEM(models.Model):
|
||||||
|
"""Model representing a PEM p12 private key created with openssl.
|
||||||
|
|
||||||
|
These are notably used as credentials for authenticating to Google
|
||||||
|
services, and Tower uses them for Google Compute Engine.
|
||||||
|
"""
|
||||||
|
filename = models.CharField(max_length=100, unique=True)
|
||||||
|
contents = BinaryField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = 'main'
|
||||||
|
|
||||||
|
|
||||||
class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
||||||
'''
|
'''
|
||||||
A credential contains information about how to talk to a remote resource
|
A credential contains information about how to talk to a remote resource
|
||||||
@@ -36,7 +51,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
|||||||
]
|
]
|
||||||
|
|
||||||
PASSWORD_FIELDS = ('password', 'ssh_key_data', 'ssh_key_unlock',
|
PASSWORD_FIELDS = ('password', 'ssh_key_data', 'ssh_key_unlock',
|
||||||
'sudo_password', 'vault_password')
|
'sudo_password', 'vault_password', 'pem_file')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
@@ -122,6 +137,12 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
|||||||
default='',
|
default='',
|
||||||
help_text=_('Vault password (or "ASK" to prompt the user).'),
|
help_text=_('Vault password (or "ASK" to prompt the user).'),
|
||||||
)
|
)
|
||||||
|
pem_file = models.FileField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
upload_to='irrelevant',
|
||||||
|
storage=storage.DatabaseStorage(PEM),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def needs_password(self):
|
def needs_password(self):
|
||||||
@@ -304,7 +325,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
|
|||||||
# If update_fields has been specified, add our field names to it,
|
# If update_fields has been specified, add our field names to it,
|
||||||
# if hit hasn't been specified, then we're just doing a normal save.
|
# if hit hasn't been specified, then we're just doing a normal save.
|
||||||
update_fields = kwargs.get('update_fields', [])
|
update_fields = kwargs.get('update_fields', [])
|
||||||
cloud = self.kind in ('aws', 'rax')
|
cloud = self.kind in ('aws', 'rax', 'gce', 'vmware', 'azure')
|
||||||
if self.cloud != cloud:
|
if self.cloud != cloud:
|
||||||
self.cloud = cloud
|
self.cloud = cloud
|
||||||
if 'cloud' not in update_fields:
|
if 'cloud' not in update_fields:
|
||||||
|
|||||||
@@ -810,6 +810,17 @@ class InventorySourceOptions(BaseModel):
|
|||||||
regions.insert(0, ('ALL', 'All'))
|
regions.insert(0, ('ALL', 'All'))
|
||||||
return regions
|
return regions
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_gce_region_choices(self):
|
||||||
|
"""Return a complete list of regions in GCE, as a list of
|
||||||
|
two-tuples.
|
||||||
|
"""
|
||||||
|
# It's not possible to get a list of regions from GCE without
|
||||||
|
# authenticating first. Therefore, use a list from settings.
|
||||||
|
regions = list(getattr(settings, 'GCE_REGION_CHOICES', []))
|
||||||
|
regions.insert(0, ('all', 'All'))
|
||||||
|
return regions
|
||||||
|
|
||||||
def clean_credential(self):
|
def clean_credential(self):
|
||||||
if not self.source:
|
if not self.source:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ GCE_REGION_CHOICES = [
|
|||||||
('us-central1-b', 'US Central (B)'),
|
('us-central1-b', 'US Central (B)'),
|
||||||
('us-central1-f', 'US Central (F)'),
|
('us-central1-f', 'US Central (F)'),
|
||||||
('europe-west1-a', 'Europe West (A)'),
|
('europe-west1-a', 'Europe West (A)'),
|
||||||
('europe-west1-b', 'Europe West (B)')
|
('europe-west1-b', 'Europe West (B)'),
|
||||||
('asia-east1-a', 'Asia East (A)'),
|
('asia-east1-a', 'Asia East (A)'),
|
||||||
('asia-east1-b', 'Asia East (B)'),
|
('asia-east1-b', 'Asia East (B)'),
|
||||||
]
|
]
|
||||||
@@ -427,7 +427,7 @@ GCE_REGIONS_BLACKLIST = []
|
|||||||
# Inventory variable name/value for determining whether a host is active
|
# Inventory variable name/value for determining whether a host is active
|
||||||
# in Google Compute Engine.
|
# in Google Compute Engine.
|
||||||
GCE_ENABLED_VAR = 'status'
|
GCE_ENABLED_VAR = 'status'
|
||||||
GCE_ENABLED_VALUE = '<???>'
|
GCE_ENABLED_VALUE = 'running'
|
||||||
|
|
||||||
# Filter for allowed group and host names when importing inventory from
|
# Filter for allowed group and host names when importing inventory from
|
||||||
# Google Compute Engine.
|
# Google Compute Engine.
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ angular.module('CredentialFormDefinition', [])
|
|||||||
'<dd>Access keys for Amazon Web Services used for inventory management or deployment.</dd>\n' +
|
'<dd>Access keys for Amazon Web Services used for inventory management or deployment.</dd>\n' +
|
||||||
'<dt>Rackspace</dt>\n' +
|
'<dt>Rackspace</dt>\n' +
|
||||||
'<dd>Access information for Rackspace Cloud used for inventory management or deployment.</dd>\n' +
|
'<dd>Access information for Rackspace Cloud used for inventory management or deployment.</dd>\n' +
|
||||||
|
'<dt>Google Compute Engine</dt>\n' +
|
||||||
|
'<dd>Credentials for Google Compute Engine, used for inventory management or deployment.</dd>\n' +
|
||||||
'<dt>VMWare</dt>\n' +
|
'<dt>VMWare</dt>\n' +
|
||||||
'<dd>Access information for VMWare vSphere used for inventory management or deployment.</dd>\n' +
|
'<dd>Access information for VMWare vSphere used for inventory management or deployment.</dd>\n' +
|
||||||
'</dl>\n'
|
'</dl>\n'
|
||||||
|
|||||||
Reference in New Issue
Block a user