Implement Azure RM creds and inventory

* Vendor ansible's azure_rm inventory script
* Add new inventory type
* Add new credential type
* Expand host instance_id column from varchar 100 to 1024 to accept the
  long instance ids returned by Azure
* Make the inventory_import azure match rename more explicit.
This commit is contained in:
Matthew Jones
2016-04-21 13:51:30 -04:00
parent 5543448a43
commit 296f70ce17
12 changed files with 943 additions and 14 deletions

View File

@@ -1,5 +1,5 @@
# Copyright (c) 2015 Ansible, Inc.
# All Rights Reserved.
CLOUD_PROVIDERS = ('azure', 'ec2', 'gce', 'rax', 'vmware', 'openstack', 'foreman', 'cloudforms')
CLOUD_PROVIDERS = ('azure', 'azure_rm', 'ec2', 'gce', 'rax', 'vmware', 'openstack', 'foreman', 'cloudforms')
SCHEDULEABLE_PROVIDERS = CLOUD_PROVIDERS + ('custom',)

View File

@@ -466,10 +466,10 @@ def load_inventory_source(source, all_group=None, group_filter_re=None,
'''
Load inventory from given source directory or file.
'''
# Sanity check: We need the "azure" module to be titled "windows_azure.py",
# because it depends on the "azure" package from PyPI, and naming the
# module the same way makes the importer sad.
source = source.replace('azure', 'windows_azure')
# Sanity check: We sanitize these module names for our API but Ansible proper doesn't follow
# good naming conventions
if source == 'azure':
source = 'windows_azure'
logger.debug('Analyzing type of source: %s', source)
original_all_group = all_group

View File

@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0018_v300_host_ordering'),
]
operations = [
migrations.AddField(
model_name='credential',
name='client',
field=models.CharField(default=b'', help_text='Client Id or Application Id for the credential', max_length=128, blank=True),
),
migrations.AddField(
model_name='credential',
name='secret',
field=models.CharField(default=b'', help_text='Secret Token for this credential', max_length=1024, blank=True),
),
migrations.AddField(
model_name='credential',
name='subscription',
field=models.CharField(default=b'', help_text='Subscription identifier for this credential', max_length=1024, blank=True),
),
migrations.AddField(
model_name='credential',
name='tenant',
field=models.CharField(default=b'', help_text='Tenant identifier for this credential', max_length=1024, blank=True),
),
migrations.AlterField(
model_name='credential',
name='kind',
field=models.CharField(default=b'ssh', max_length=32, choices=[(b'ssh', 'Machine'), (b'net', 'Network'), (b'scm', 'Source Control'), (b'aws', 'Amazon Web Services'), (b'rax', 'Rackspace'), (b'vmware', 'VMware vCenter'), (b'foreman', 'Satellite 6'), (b'cloudforms', 'CloudForms'), (b'gce', 'Google Compute Engine'), (b'azure', 'Microsoft Azure Classic (deprecated)'), (b'azure_rm', 'Microsoft Azure Resource Manager'), (b'openstack', 'OpenStack')]),
),
migrations.AlterField(
model_name='host',
name='instance_id',
field=models.CharField(default=b'', max_length=1024, blank=True),
),
migrations.AlterField(
model_name='inventorysource',
name='source',
field=models.CharField(default=b'', max_length=32, blank=True, choices=[(b'', 'Manual'), (b'file', 'Local File, Directory or Script'), (b'rax', 'Rackspace Cloud Servers'), (b'ec2', 'Amazon EC2'), (b'gce', 'Google Compute Engine'), (b'azure', 'Microsoft Azure Classic (deprecated)'), (b'azure_rm', 'Microsoft Azure Resource Manager'), (b'vmware', 'VMware vCenter'), (b'foreman', 'Satellite 6'), (b'cloudforms', 'CloudForms'), (b'openstack', 'OpenStack'), (b'custom', 'Custom Script')]),
),
migrations.AlterField(
model_name='inventoryupdate',
name='source',
field=models.CharField(default=b'', max_length=32, blank=True, choices=[(b'', 'Manual'), (b'file', 'Local File, Directory or Script'), (b'rax', 'Rackspace Cloud Servers'), (b'ec2', 'Amazon EC2'), (b'gce', 'Google Compute Engine'), (b'azure', 'Microsoft Azure Classic (deprecated)'), (b'azure_rm', 'Microsoft Azure Resource Manager'), (b'vmware', 'VMware vCenter'), (b'foreman', 'Satellite 6'), (b'cloudforms', 'CloudForms'), (b'openstack', 'OpenStack'), (b'custom', 'Custom Script')]),
),
]

View File

@@ -61,7 +61,7 @@ PERMISSION_TYPE_CHOICES = [
(PERM_JOBTEMPLATE_CREATE, _('Create a Job Template')),
]
CLOUD_INVENTORY_SOURCES = ['ec2', 'rax', 'vmware', 'gce', 'azure', 'openstack', 'custom', 'foreman', 'cloudforms']
CLOUD_INVENTORY_SOURCES = ['ec2', 'rax', 'vmware', 'gce', 'azure', 'azure_rm', 'openstack', 'custom', 'foreman', 'cloudforms']
VERBOSITY_CHOICES = [
(0, '0 (Normal)'),

View File

@@ -41,7 +41,8 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
('foreman', _('Satellite 6')),
('cloudforms', _('CloudForms')),
('gce', _('Google Compute Engine')),
('azure', _('Microsoft Azure')),
('azure', _('Microsoft Azure Classic (deprecated)')),
('azure_rm', _('Microsoft Azure Resource Manager')),
('openstack', _('OpenStack')),
]
@@ -55,7 +56,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
]
PASSWORD_FIELDS = ('password', 'security_token', 'ssh_key_data', 'ssh_key_unlock',
'become_password', 'vault_password')
'become_password', 'vault_password', 'secret')
class Meta:
app_label = 'main'
@@ -168,6 +169,30 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
default='',
help_text=_('Vault password (or "ASK" to prompt the user).'),
)
client = models.CharField(
max_length=128,
blank=True,
default='',
help_text=_('Client Id or Application Id for the credential'),
)
secret = models.CharField(
max_length=1024,
blank=True,
default='',
help_text=_('Secret Token for this credential'),
)
subscription = models.CharField(
max_length=1024,
blank=True,
default='',
help_text=_('Subscription identifier for this credential'),
)
tenant = models.CharField(
max_length=1024,
blank=True,
default='',
help_text=_('Tenant identifier for this credential'),
)
owner_role = ImplicitRoleField(
role_name='Credential Owner',
role_description='Owner of the credential',

View File

@@ -355,7 +355,7 @@ class Host(CommonModelNameNotUnique, ResourceMixin):
help_text=_('Is this host online and available for running jobs?'),
)
instance_id = models.CharField(
max_length=100,
max_length=1024,
blank=True,
default='',
)
@@ -748,7 +748,8 @@ class InventorySourceOptions(BaseModel):
('rax', _('Rackspace Cloud Servers')),
('ec2', _('Amazon EC2')),
('gce', _('Google Compute Engine')),
('azure', _('Microsoft Azure')),
('azure', _('Microsoft Azure Classic (deprecated)')),
('azure_rm', _('Microsoft Azure Resource Manager')),
('vmware', _('VMware vCenter')),
('foreman', _('Satellite 6')),
('cloudforms', _('CloudForms')),
@@ -969,6 +970,10 @@ class InventorySourceOptions(BaseModel):
regions.insert(0, ('all', 'All'))
return regions
@classmethod
def get_azure_rm_region_choices(self):
return InventorySourceOptions.get_azure_region_choices()
@classmethod
def get_vmware_region_choices(self):
"""Return a complete list of regions in VMware, as a list of two-tuples

View File

@@ -801,6 +801,16 @@ class RunJob(BaseTask):
elif cloud_cred and cloud_cred.kind == 'azure':
env['AZURE_SUBSCRIPTION_ID'] = cloud_cred.username
env['AZURE_CERT_PATH'] = kwargs.get('private_data_files', {}).get('cloud_credential', '')
elif cloud_cred and cloud_cred.kind == 'azure_rm':
if len(cloud_cred.client) and len(cloud_cred.tenant):
env['AZURE_CLIENT_ID'] = cloud_cred.client
env['AZURE_SECRET'] = decrypt_field(cloud_cred, 'secret')
env['AZURE_TENANT'] = cloud_cred.tenant
env['AZURE_SUBSCRIPTION_ID'] = cloud_cred.subscription
else:
env['AZURE_SUBSCRIPTION_ID'] = cloud_cred.subscription
env['AZURE_AD_USER'] = cloud_cred.username
env['AZURE_PASSWORD'] = decrypt_field(cloud_cred, 'password')
elif cloud_cred and cloud_cred.kind == 'vmware':
env['VMWARE_USER'] = cloud_cred.username
env['VMWARE_PASSWORD'] = decrypt_field(cloud_cred, 'password')
@@ -1278,6 +1288,14 @@ class RunInventoryUpdate(BaseTask):
cp.set(section, 'username', credential.username)
cp.set(section, 'password', decrypt_field(credential, 'password'))
elif inventory_update.source == 'azure_rm':
section = 'azure'
cp.add_section(section)
cp.set(section, 'include_powerstate', 'yes')
cp.set(section, 'group_by_resource_group', 'yes')
cp.set(section, 'group_by_location', 'yes')
cp.set(section, 'group_by_tag', 'yes')
# Return INI content.
if cp.sections():
f = cStringIO.StringIO()
@@ -1298,9 +1316,9 @@ class RunInventoryUpdate(BaseTask):
# passwords dictionary.
credential = inventory_update.credential
if credential:
for subkey in ('username', 'host', 'project'):
for subkey in ('username', 'host', 'project', 'client', 'tenant', 'subscription'):
passwords['source_%s' % subkey] = getattr(credential, subkey)
for passkey in ('password', 'ssh_key_data', 'security_token'):
for passkey in ('password', 'ssh_key_data', 'security_token', 'secret'):
k = 'source_%s' % passkey
passwords[k] = decrypt_field(credential, passkey)
return passwords
@@ -1353,6 +1371,17 @@ class RunInventoryUpdate(BaseTask):
elif inventory_update.source == 'azure':
env['AZURE_SUBSCRIPTION_ID'] = passwords.get('source_username', '')
env['AZURE_CERT_PATH'] = cloud_credential
elif inventory_update.source == 'azure_rm':
if len(passwords.get('source_client', '')) and \
len(passwords.get('source_tenant', '')):
env['AZURE_CLIENT_ID'] = passwords.get('source_client', '')
env['AZURE_SECRET'] = passwords.get('source_secret', '')
env['AZURE_TENANT'] = passwords.get('source_tenant', '')
env['AZURE_SUBSCRIPTION_ID'] = passwords.get('source_subscription', '')
else:
env['AZURE_SUBSCRIPTION_ID'] = passwords.get('source_subscription', '')
env['AZURE_AD_USER'] = passwords.get('source_username', '')
env['AZURE_PASSWORD'] = passwords.get('source_password', '')
elif inventory_update.source == 'gce':
env['GCE_EMAIL'] = passwords.get('source_username', '')
env['GCE_PROJECT'] = passwords.get('source_project', '')