AC-687 Add ec2/rax region choices to inventory source options response, add validation for source_regions.

This commit is contained in:
Chris Church 2013-11-20 15:19:44 -05:00
parent 7929cfedd8
commit 8bf198f7ed
4 changed files with 199 additions and 0 deletions

View File

@ -677,6 +677,13 @@ class InventorySourceSerializer(BaseSerializer):
# FIXME
return attrs
def metadata(self):
metadata = super(InventorySourceSerializer, self).metadata()
field_opts = metadata.get('source_regions', {})
field_opts['ec2_region_choices'] = self.opts.model.get_ec2_region_choices()
field_opts['rax_region_choices'] = self.opts.model.get_rax_region_choices()
return metadata
class InventoryUpdateSerializer(BaseSerializer):

View File

@ -561,6 +561,36 @@ class InventorySource(PrimordialModel):
editable=False,
)
@classmethod
def get_ec2_region_choices(cls):
ec2_region_names = getattr(settings, 'EC2_REGION_NAMES', {})
ec2_name_replacements = {
'us': 'US',
'ap': 'Asia Pacific',
'eu': 'Europe',
'sa': 'South America',
}
import boto.ec2
regions = [('all', 'All')]
for region in boto.ec2.regions():
label = ec2_region_names.get(region.name, '')
if not label:
label_parts = []
for part in region.name.split('-'):
part = ec2_name_replacements.get(part.lower(), part.title())
label_parts.append(part)
label = ' '.join(label_parts)
regions.append((region.name, label))
return regions
@classmethod
def get_rax_region_choices(cls):
# Not possible to get rax regions without first authenticating, so use
# list from settings.
regions = list(getattr(settings, 'RAX_REGION_CHOICES', []))
regions.insert(0, ('ALL', 'All'))
return regions
def clean_credential(self):
if not self.source:
return None
@ -576,6 +606,31 @@ class InventorySource(PrimordialModel):
raise ValidationError('Credential is required for a cloud source')
return cred
def clean_source_regions(self):
regions = self.source_regions
if self.source == 'ec2':
valid_regions = [x[0] for x in self.get_ec2_region_choices()]
region_transform = lambda x: x.strip().lower()
elif self.source == 'rax':
valid_regions = [x[0] for x in self.get_rax_region_choices()]
region_transform = lambda x: x.strip().upper()
else:
return ''
all_region = region_transform('all')
valid_regions = [region_transform(x) for x in valid_regions]
regions = [region_transform(x) for x in regions.split(',') if x.strip()]
if all_region in regions:
return all_region
invalid_regions = []
for r in regions:
if r not in valid_regions and r not in invalid_regions:
invalid_regions.append(r)
if invalid_regions:
raise ValidationError('Invalid %s region%s: %s' % (self.source,
'' if len(invalid_regions) == 1 else 's',
', '.join(invalid_regions)))
return ','.join(regions)
def save(self, *args, **kwargs):
new_instance = not bool(self.pk)
# If update_fields has been specified, add our field names to it,

View File

@ -1030,6 +1030,116 @@ class InventoryUpdatesTest(BaseTransactionTest):
self.assertFalse(re.match(r'^i-[0-9a-f]{8}$', group.name, re.I),
group.name)
def test_put_inventory_source_detail_with_regions(self):
creds_url = reverse('api:credential_list')
inv_src_url1 = reverse('api:inventory_source_detail',
args=(self.group.inventory_source.pk,))
inv_src_url2 = reverse('api:inventory_source_detail',
args=(self.group2.inventory_source.pk,))
# Create an AWS credential to use for first inventory source.
aws_cred_data = {
'name': 'AWS key that does not need to have valid info because '
'we do not care if the update actually succeeds',
'kind': 'aws',
'user': self.super_django_user.pk,
'username': 'aws access key id goes here',
'password': 'aws secret access key goes here',
}
with self.current_user(self.super_django_user):
aws_cred_response = self.post(creds_url, aws_cred_data, expect=201)
aws_cred_id = aws_cred_response['id']
# Create a RAX credential to use for second inventory source.
rax_cred_data = {
'name': 'RAX cred that does not need to have valid info because '
'we do not care if the update actually succeeds',
'kind': 'rax',
'user': self.super_django_user.pk,
'username': 'rax username',
'password': 'rax api key',
}
with self.current_user(self.super_django_user):
rax_cred_response = self.post(creds_url, rax_cred_data, expect=201)
rax_cred_id = rax_cred_response['id']
# Verify the options request gives ec2 and rax region choices.
with self.current_user(self.super_django_user):
response = self.options(inv_src_url1, expect=200)
self.assertTrue('ec2_region_choices' in response['actions']['GET']['source_regions'])
self.assertTrue('rax_region_choices' in response['actions']['GET']['source_regions'])
# Updaate the first inventory source to use EC2 with empty regions.
inv_src_data = {
'source': 'ec2',
'credential': aws_cred_id,
'source_regions': '',
}
with self.current_user(self.super_django_user):
response = self.put(inv_src_url1, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], '')
# All region.
inv_src_data['source_regions'] = 'ALL'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url1, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], 'all')
# Invalid region.
inv_src_data['source_regions'] = 'us-north-99'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url1, inv_src_data, expect=400)
# All takes precedence over any other regions.
inv_src_data['source_regions'] = 'us-north-99,,all'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url1, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], 'all')
# Valid region.
inv_src_data['source_regions'] = 'us-west-1'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url1, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], 'us-west-1')
# Invalid region (along with valid one).
inv_src_data['source_regions'] = 'us-west-1, us-north-99'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url1, inv_src_data, expect=400)
# Valid regions.
inv_src_data['source_regions'] = 'us-west-1, us-east-1, '
with self.current_user(self.super_django_user):
response = self.put(inv_src_url1, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], 'us-west-1,us-east-1')
# Updaate the second inventory source to use RAX with empty regions.
inv_src_data = {
'source': 'rax',
'credential': rax_cred_id,
'source_regions': '',
}
with self.current_user(self.super_django_user):
response = self.put(inv_src_url2, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], '')
# All region.
inv_src_data['source_regions'] = 'all'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url2, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], 'ALL')
# Invalid region.
inv_src_data['source_regions'] = 'RDU'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url2, inv_src_data, expect=400)
# All takes precedence over any other regions.
inv_src_data['source_regions'] = 'RDU,,all'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url2, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], 'ALL')
# Valid region.
inv_src_data['source_regions'] = 'dfw'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url2, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], 'DFW')
# Invalid region (along with valid one).
inv_src_data['source_regions'] = 'dfw, rdu'
with self.current_user(self.super_django_user):
response = self.put(inv_src_url2, inv_src_data, expect=400)
# Valid regions.
inv_src_data['source_regions'] = 'ORD, iad, '
with self.current_user(self.super_django_user):
response = self.put(inv_src_url2, inv_src_data, expect=200)
self.assertEqual(response['source_regions'], 'ORD,IAD')
def test_post_inventory_source_update(self):
creds_url = reverse('api:credential_list')
inv_src_url = reverse('api:inventory_source_detail',

View File

@ -292,6 +292,33 @@ ANSIBLE_PARAMIKO_RECORD_HOST_KEYS = False
# the celery task.
AWX_TASK_ENV = {}
# Not possible to get list of regions without authenticating, so use this list
# instead (based on docs from:
# http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/Service_Access_Endpoints-d1e517.html)
RAX_REGION_CHOICES = [
('ORD', 'Chicago'),
('DFW', 'Dallas/Ft. Worth'),
('IAD', 'Northern Virginia'),
('LON', 'London'),
('SYD', 'Sydney'),
('HKG', 'Hong Kong'),
]
# AWS does not appear to provide pretty region names via any API, so store the
# list of names here. The available region IDs will be pulled from boto.
# http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region
EC2_REGION_NAMES = {
'us-east-1': 'US East (Northern Virginia)',
'us-west-2': 'US West (Oregon)',
'us-west-1': 'US West (Northern California)',
'eu-west-1': 'EU (Ireland)',
'ap-southeast-1': 'Asia Pacific (Singapore)',
'ap-southeast-2': 'Asia Pacific (Sydney)',
'ap-northeast-1': 'Asia Pacific (Tokyo)',
'sa-east-1': 'South America (Sao Paulo)',
'us-gov-west-1': 'US West (GovCloud)',
}
# Internal API URL for use by inventory scripts and callback plugin.
if 'devserver' in INSTALLED_APPS:
INTERNAL_API_URL = 'http://127.0.0.1:%s' % DEVSERVER_DEFAULT_PORT