From c807d5dcf373cc84bbac34b21db09a1746b46ae7 Mon Sep 17 00:00:00 2001
From: AlanCoding
Date: Tue, 1 Mar 2016 09:33:17 -0500
Subject: [PATCH 1/3] Add keystone v3 support via new domain field on
credential
---
awx/api/serializers.py | 3 ++-
awx/main/migrations/0007_v300_changes.py | 19 +++++++++++++++++++
awx/main/models/credential.py | 7 +++++++
awx/main/tasks.py | 4 ++++
awx/main/tests/old/inventory.py | 20 ++++++++++++++++++++
5 files changed, 52 insertions(+), 1 deletion(-)
create mode 100644 awx/main/migrations/0007_v300_changes.py
diff --git a/awx/api/serializers.py b/awx/api/serializers.py
index 6ca73cf6a0..e48f12e6c4 100644
--- a/awx/api/serializers.py
+++ b/awx/api/serializers.py
@@ -1484,7 +1484,8 @@ class CredentialSerializer(BaseSerializer):
class Meta:
model = Credential
fields = ('*', 'user', 'team', 'kind', 'cloud', 'host', 'username',
- 'password', 'security_token', 'project', 'ssh_key_data', 'ssh_key_unlock',
+ 'password', 'security_token', 'project', 'domain',
+ 'ssh_key_data', 'ssh_key_unlock',
'become_method', 'become_username', 'become_password',
'vault_password')
diff --git a/awx/main/migrations/0007_v300_changes.py b/awx/main/migrations/0007_v300_changes.py
new file mode 100644
index 0000000000..f6d0ec1410
--- /dev/null
+++ b/awx/main/migrations/0007_v300_changes.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('main', '0006_v300_changes'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='credential',
+ name='domain',
+ field=models.CharField(default=b'', help_text='The identifier for the domain.', max_length=100, verbose_name='Domain', blank=True),
+ ),
+ ]
diff --git a/awx/main/models/credential.py b/awx/main/models/credential.py
index 82e0f576e1..328c738cc0 100644
--- a/awx/main/models/credential.py
+++ b/awx/main/models/credential.py
@@ -114,6 +114,13 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
verbose_name=_('Project'),
help_text=_('The identifier for the project.'),
)
+ domain = models.CharField(
+ blank=True,
+ default='',
+ max_length=100,
+ verbose_name=_('Domain'),
+ help_text=_('The identifier for the domain.'),
+ )
ssh_key_data = models.TextField(
blank=True,
default='',
diff --git a/awx/main/tasks.py b/awx/main/tasks.py
index ec34886632..ba54e17e9b 100644
--- a/awx/main/tasks.py
+++ b/awx/main/tasks.py
@@ -712,6 +712,8 @@ class RunJob(BaseTask):
username=credential.username,
password=decrypt_field(credential, "password"),
project_name=credential.project)
+ if credential.domain not in (None, ''):
+ openstack_auth['domain_name'] = credential.domain
openstack_data = {
'clouds': {
'devstack': {
@@ -1151,6 +1153,8 @@ class RunInventoryUpdate(BaseTask):
username=credential.username,
password=decrypt_field(credential, "password"),
project_name=credential.project)
+ if credential.domain not in (None, ''):
+ openstack_auth['domain_name'] = credential.domain
private_state = str(inventory_update.source_vars_dict.get('private', 'true'))
# Retrieve cache path from inventory update vars if available,
# otherwise create a temporary cache path only for this update.
diff --git a/awx/main/tests/old/inventory.py b/awx/main/tests/old/inventory.py
index 5c48f30bb6..ccfb7138c2 100644
--- a/awx/main/tests/old/inventory.py
+++ b/awx/main/tests/old/inventory.py
@@ -2068,6 +2068,26 @@ class InventoryUpdatesTest(BaseTransactionTest):
self.check_inventory_source(inventory_source)
self.assertFalse(self.group.all_hosts.filter(instance_id='').exists())
+ def test_update_from_openstack_v3(self):
+ # Check that update works with Keystone v3 identity service
+ api_url = getattr(settings, 'TEST_OPENSTACK_HOST_V3', '')
+ api_user = getattr(settings, 'TEST_OPENSTACK_USER', '')
+ api_password = getattr(settings, 'TEST_OPENSTACK_PASSWORD', '')
+ api_project = getattr(settings, 'TEST_OPENSTACK_PROJECT', '')
+ api_domain = getattr(settings, 'TEST_OPENSTACK_DOMAIN', '')
+ if not all([api_url, api_user, api_password, api_project, api_domain]):
+ self.skipTest("No test openstack v3 credentials defined")
+ self.create_test_license_file()
+ credential = Credential.objects.create(kind='openstack',
+ host=api_url,
+ username=api_user,
+ password=api_password,
+ project=api_project,
+ domain=api_domain)
+ inventory_source = self.update_inventory_source(self.group, source='openstack', credential=credential)
+ self.check_inventory_source(inventory_source)
+ self.assertFalse(self.group.all_hosts.filter(instance_id='').exists())
+
def test_update_from_azure(self):
source_username = getattr(settings, 'TEST_AZURE_USERNAME', '')
source_key_data = getattr(settings, 'TEST_AZURE_KEY_DATA', '')
From f4b1de766dd0670cd9243088ee1e9525d1fd415f Mon Sep 17 00:00:00 2001
From: AlanCoding
Date: Wed, 16 Mar 2016 15:32:05 -0400
Subject: [PATCH 2/3] Adding OpenStack v3 cred type
---
awx/main/constants.py | 2 +-
awx/main/models/base.py | 2 +-
awx/main/models/credential.py | 18 ++++++++++----
awx/main/models/inventory.py | 6 +++++
awx/main/tasks.py | 13 ++++++----
awx/main/tests/old/inventory.py | 28 ++++++++++++++++++++--
awx/ui/client/src/forms/Credentials.js | 23 +++++++++++++++---
awx/ui/client/src/forms/Source.js | 3 ++-
awx/ui/client/src/helpers/Credentials.js | 19 +++++++++++++++
awx/ui/client/src/helpers/Groups.js | 12 ++++++----
awx/ui/client/src/lists/HomeGroups.js | 5 +++-
awx/ui/client/src/lists/InventoryGroups.js | 5 +++-
12 files changed, 114 insertions(+), 22 deletions(-)
diff --git a/awx/main/constants.py b/awx/main/constants.py
index 64f6265569..a6bdafdf5a 100644
--- a/awx/main/constants.py
+++ b/awx/main/constants.py
@@ -1,5 +1,5 @@
# Copyright (c) 2015 Ansible, Inc.
# All Rights Reserved.
-CLOUD_PROVIDERS = ('azure', 'ec2', 'gce', 'rax', 'vmware', 'openstack')
+CLOUD_PROVIDERS = ('azure', 'ec2', 'gce', 'rax', 'vmware', 'openstack', 'openstack_v3')
SCHEDULEABLE_PROVIDERS = CLOUD_PROVIDERS + ('custom',)
diff --git a/awx/main/models/base.py b/awx/main/models/base.py
index c4edfbd8ba..b912a71572 100644
--- a/awx/main/models/base.py
+++ b/awx/main/models/base.py
@@ -56,7 +56,7 @@ PERMISSION_TYPE_CHOICES = [
(PERM_JOBTEMPLATE_CREATE, _('Create a Job Template')),
]
-CLOUD_INVENTORY_SOURCES = ['ec2', 'rax', 'vmware', 'gce', 'azure', 'openstack', 'custom']
+CLOUD_INVENTORY_SOURCES = ['ec2', 'rax', 'vmware', 'gce', 'azure', 'openstack', 'openstack_v3', 'custom']
VERBOSITY_CHOICES = [
(0, '0 (Normal)'),
diff --git a/awx/main/models/credential.py b/awx/main/models/credential.py
index 328c738cc0..0293d18e00 100644
--- a/awx/main/models/credential.py
+++ b/awx/main/models/credential.py
@@ -34,6 +34,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
('gce', _('Google Compute Engine')),
('azure', _('Microsoft Azure')),
('openstack', _('OpenStack')),
+ ('openstack_v3', _('OpenStack V3')),
]
BECOME_METHOD_CHOICES = [
@@ -210,10 +211,19 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
host = self.host or ''
if not host and self.kind == 'vmware':
raise ValidationError('Host required for VMware credential.')
- if not host and self.kind == 'openstack':
+ if not host and self.kind in ('openstack', 'openstack_v3'):
raise ValidationError('Host required for OpenStack credential.')
return host
+ def clean_domain(self):
+ """For case of Keystone v3 identity service that requires a
+ `domain`, that a domain is provided.
+ """
+ domain = self.domain or ''
+ if not domain and self.kind == 'openstack_v3':
+ raise ValidationError('Domain required for OpenStack with Keystone v3.')
+ return domain
+
def clean_username(self):
username = self.username or ''
if not username and self.kind == 'aws':
@@ -223,7 +233,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
'credential.')
if not username and self.kind == 'vmware':
raise ValidationError('Username required for VMware credential.')
- if not username and self.kind == 'openstack':
+ if not username and self.kind in ('openstack', 'openstack_v3'):
raise ValidationError('Username required for OpenStack credential.')
return username
@@ -235,13 +245,13 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique):
raise ValidationError('API key required for Rackspace credential.')
if not password and self.kind == 'vmware':
raise ValidationError('Password required for VMware credential.')
- if not password and self.kind == 'openstack':
+ if not password and self.kind in ('openstack', 'openstack_v3'):
raise ValidationError('Password or API key required for OpenStack credential.')
return password
def clean_project(self):
project = self.project or ''
- if self.kind == 'openstack' and not project:
+ if self.kind in ('openstack', 'openstack_v3') and not project:
raise ValidationError('Project name required for OpenStack credential.')
return project
diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py
index c95c8488bd..e1dd36e64d 100644
--- a/awx/main/models/inventory.py
+++ b/awx/main/models/inventory.py
@@ -748,6 +748,7 @@ class InventorySourceOptions(BaseModel):
('azure', _('Microsoft Azure')),
('vmware', _('VMware vCenter')),
('openstack', _('OpenStack')),
+ ('openstack_v3', _('OpenStack V3')),
('custom', _('Custom Script')),
]
@@ -976,6 +977,11 @@ class InventorySourceOptions(BaseModel):
"""I don't think openstack has regions"""
return [('all', 'All')]
+ @classmethod
+ def get_openstack_v3_region_choices(self):
+ """Defer to the behavior of openstack"""
+ return self.get_openstack_region_choices()
+
def clean_credential(self):
if not self.source:
return None
diff --git a/awx/main/tasks.py b/awx/main/tasks.py
index ba54e17e9b..381ea31623 100644
--- a/awx/main/tasks.py
+++ b/awx/main/tasks.py
@@ -706,7 +706,7 @@ class RunJob(BaseTask):
if credential.ssh_key_data not in (None, ''):
private_data[cred_name] = decrypt_field(credential, 'ssh_key_data') or ''
- if job.cloud_credential and job.cloud_credential.kind == 'openstack':
+ if job.cloud_credential and job.cloud_credential.kind in ('openstack', 'openstack_v3'):
credential = job.cloud_credential
openstack_auth = dict(auth_url=credential.host,
username=credential.username,
@@ -798,7 +798,7 @@ class RunJob(BaseTask):
env['VMWARE_USER'] = cloud_cred.username
env['VMWARE_PASSWORD'] = decrypt_field(cloud_cred, 'password')
env['VMWARE_HOST'] = cloud_cred.host
- elif cloud_cred and cloud_cred.kind == 'openstack':
+ elif cloud_cred and cloud_cred.kind in ('openstack', 'openstack_v3'):
env['OS_CLIENT_CONFIG_FILE'] = kwargs.get('private_data_files', {}).get('cloud_credential', '')
# Set environment variables related to scan jobs
@@ -1147,7 +1147,7 @@ class RunInventoryUpdate(BaseTask):
credential = inventory_update.credential
return dict(cloud_credential=decrypt_field(credential, 'ssh_key_data'))
- if inventory_update.source == 'openstack':
+ if inventory_update.source in ('openstack', 'openstack_v3'):
credential = inventory_update.credential
openstack_auth = dict(auth_url=credential.host,
username=credential.username,
@@ -1302,7 +1302,7 @@ class RunInventoryUpdate(BaseTask):
env['GCE_PROJECT'] = passwords.get('source_project', '')
env['GCE_PEM_FILE_PATH'] = cloud_credential
env['GCE_ZONE'] = inventory_update.source_regions
- elif inventory_update.source == 'openstack':
+ elif inventory_update.source in ('openstack', 'openstack_v3'):
env['OS_CLIENT_CONFIG_FILE'] = cloud_credential
elif inventory_update.source == 'file':
# FIXME: Parse source_env to dict, update env.
@@ -1345,6 +1345,11 @@ class RunInventoryUpdate(BaseTask):
# to a shorter variable. :)
src = inventory_update.source
+ # OpenStack V3 has everything in common with OpenStack aside
+ # from one extra parameter, so share these resources between them.
+ if src == 'openstack_v3':
+ src = 'openstack'
+
# Get the path to the inventory plugin, and append it to our
# arguments.
plugin_path = self.get_path_to('..', 'plugins', 'inventory',
diff --git a/awx/main/tests/old/inventory.py b/awx/main/tests/old/inventory.py
index ccfb7138c2..3ac2310160 100644
--- a/awx/main/tests/old/inventory.py
+++ b/awx/main/tests/old/inventory.py
@@ -2078,13 +2078,13 @@ class InventoryUpdatesTest(BaseTransactionTest):
if not all([api_url, api_user, api_password, api_project, api_domain]):
self.skipTest("No test openstack v3 credentials defined")
self.create_test_license_file()
- credential = Credential.objects.create(kind='openstack',
+ credential = Credential.objects.create(kind='openstack_v3',
host=api_url,
username=api_user,
password=api_password,
project=api_project,
domain=api_domain)
- inventory_source = self.update_inventory_source(self.group, source='openstack', credential=credential)
+ inventory_source = self.update_inventory_source(self.group, source='openstack_v3', credential=credential)
self.check_inventory_source(inventory_source)
self.assertFalse(self.group.all_hosts.filter(instance_id='').exists())
@@ -2132,3 +2132,27 @@ class InventoryCredentialTest(BaseTest):
self.assertIn('password', response)
self.assertIn('host', response)
self.assertIn('project', response)
+
+ def test_openstack_v3_create_ok(self):
+ data = {
+ 'kind': 'openstack_v3',
+ 'name': 'Best credential ever',
+ 'username': 'some_user',
+ 'password': 'some_password',
+ 'project': 'some_project',
+ 'host': 'some_host',
+ 'domain': 'some_domain',
+ }
+ self.post(self.url, data=data, expect=201, auth=self.get_super_credentials())
+
+ def test_openstack_v3_create_fail_required_fields(self):
+ data = {
+ 'kind': 'openstack_v3',
+ 'name': 'Best credential ever',
+ }
+ response = self.post(self.url, data=data, expect=400, auth=self.get_super_credentials())
+ self.assertIn('username', response)
+ self.assertIn('password', response)
+ self.assertIn('host', response)
+ self.assertIn('project', response)
+ self.assertIn('domain', response)
diff --git a/awx/ui/client/src/forms/Credentials.js b/awx/ui/client/src/forms/Credentials.js
index 221ab12b22..84aaf804e8 100644
--- a/awx/ui/client/src/forms/Credentials.js
+++ b/awx/ui/client/src/forms/Credentials.js
@@ -169,7 +169,7 @@ export default
"host": {
labelBind: 'hostLabel',
type: 'text',
- ngShow: "kind.value == 'vmware' || kind.value == 'openstack'",
+ ngShow: "kind.value == 'vmware' || kind.value == 'openstack' || kind.value === 'openstack_v3'",
awPopOverWatch: "hostPopOver",
awPopOver: "set in helpers/credentials",
dataTitle: 'Host',
@@ -243,7 +243,7 @@ export default
"password": {
labelBind: 'passwordLabel',
type: 'sensitive',
- ngShow: "kind.value == 'scm' || kind.value == 'vmware' || kind.value == 'openstack'",
+ ngShow: "kind.value == 'scm' || kind.value == 'vmware' || kind.value == 'openstack' || kind.value == 'openstack_v3'",
addRequired: false,
editRequired: false,
ask: false,
@@ -338,7 +338,7 @@ export default
"project": {
labelBind: 'projectLabel',
type: 'text',
- ngShow: "kind.value == 'gce' || kind.value == 'openstack'",
+ ngShow: "kind.value == 'gce' || kind.value == 'openstack' || kind.value == 'openstack_v3'",
awPopOverWatch: "projectPopOver",
awPopOver: "set in helpers/credentials",
dataTitle: 'Project ID',
@@ -352,6 +352,23 @@ export default
},
subForm: 'credentialSubForm'
},
+ "domain": {
+ labelBind: 'domainLabel',
+ type: 'text',
+ ngShow: "kind.value == 'openstack_v3'",
+ awPopOverWatch: "domainPopOver",
+ awPopOver: "set in helpers/credentials",
+ dataTitle: 'Domain Name',
+ dataPlacement: 'right',
+ dataContainer: "body",
+ addRequired: false,
+ editRequired: false,
+ awRequiredWhen: {
+ variable: 'domain_required',
+ init: false
+ },
+ subForm: 'credentialSubForm'
+ },
"vault_password": {
label: "Vault Password",
type: 'sensitive',
diff --git a/awx/ui/client/src/forms/Source.js b/awx/ui/client/src/forms/Source.js
index 1eee07b344..86e6db5477 100644
--- a/awx/ui/client/src/forms/Source.js
+++ b/awx/ui/client/src/forms/Source.js
@@ -169,7 +169,8 @@ export default
label: 'Source Variables', //"{{vars_label}}" ,
ngShow: "source && (source.value == 'vmware' || " +
- "source.value == 'openstack')",
+ "source.value == 'openstack' || " +
+ "source.value == 'openstack_v3')",
type: 'textarea',
addRequired: false,
class: 'Form-textAreaLabel',
diff --git a/awx/ui/client/src/helpers/Credentials.js b/awx/ui/client/src/helpers/Credentials.js
index f986f06e4e..f1af37a011 100644
--- a/awx/ui/client/src/helpers/Credentials.js
+++ b/awx/ui/client/src/helpers/Credentials.js
@@ -62,6 +62,7 @@ angular.module('CredentialsHelper', ['Utilities'])
scope.username_required = false; // JT-- added username_required b/c mutliple 'kinds' need username to be required (GCE)
scope.key_required = false; // JT -- doing the same for key and project
scope.project_required = false;
+ scope.domain_required = false;
scope.subscription_required = false;
scope.key_description = "Paste the contents of the SSH private key file.";
scope.key_hint= "drag and drop an SSH private key file on the field below";
@@ -69,9 +70,11 @@ angular.module('CredentialsHelper', ['Utilities'])
scope.password_required = false;
scope.hostLabel = '';
scope.projectLabel = '';
+ scope.domainLabel = '';
scope.project_required = false;
scope.passwordLabel = 'Password (API Key)';
scope.projectPopOver = "The project value
";
+ scope.domainPopOver = "The domain name
";
scope.hostPopOver = "The host value
";
if (!Empty(scope.kind)) {
@@ -133,6 +136,22 @@ angular.module('CredentialsHelper', ['Utilities'])
" as the username.
";
scope.hostPopOver = "The host to authenticate with." +
"
For example, https://openstack.business.com/v2.0/";
+ case 'openstack_v3':
+ scope.hostLabel = "Host (Authentication URL)";
+ scope.projectLabel = "Project (Tenet Name/ID)";
+ scope.domainLabel = "Domain Name";
+ scope.password_required = true;
+ scope.project_required = true;
+ scope.domain_required = true;
+ scope.host_required = true;
+ scope.username_required = true;
+ scope.projectPopOver = "
This is the tenant name " +
+ "or tenant id. This value is usually the same " +
+ " as the username.
";
+ scope.hostPopOver = "The host to authenticate with." +
+ "
For example, https://openstack.business.com/v3
";
+ scope.domainPopOver = "Domain used for Keystone v3 " +
+ "
identity service.
";
break;
}
}
diff --git a/awx/ui/client/src/helpers/Groups.js b/awx/ui/client/src/helpers/Groups.js
index eeebb9d8bf..4e95d96857 100644
--- a/awx/ui/client/src/helpers/Groups.js
+++ b/awx/ui/client/src/helpers/Groups.js
@@ -305,7 +305,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
field_id: 'source_extra_vars', onReady: callback });
}
if(scope.source.value==="vmware" ||
- scope.source.value==="openstack"){
+ scope.source.value==="openstack" ||
+ scope.source.value==="openstack_v3"){
scope.inventory_variables = (Empty(scope.source_vars)) ? "---" : scope.source_vars;
ParseTypeChange({ scope: scope, variable: 'inventory_variables', parse_variable: form.fields.inventory_variables.parseTypeName,
field_id: 'source_inventory_variables', onReady: callback });
@@ -315,7 +316,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
scope.source.value==='gce' ||
scope.source.value === 'azure' ||
scope.source.value === 'vmware' ||
- scope.source.value === 'openstack') {
+ scope.source.value === 'openstack' ||
+ scope.source.value === 'openstack_v3') {
if (scope.source.value === 'ec2') {
kind = 'aws';
} else {
@@ -924,7 +926,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
ParseTypeChange({ scope: sources_scope, variable: 'source_vars', parse_variable: SourceForm.fields.source_vars.parseTypeName,
field_id: 'source_source_vars', onReady: waitStop });
} else if (sources_scope.source && (sources_scope.source.value === 'vmware' ||
- sources_scope.source.value === 'openstack')) {
+ sources_scope.source.value === 'openstack' ||
+ sources_scope.source.value === 'openstack_v3')) {
Wait('start');
ParseTypeChange({ scope: sources_scope, variable: 'inventory_variables', parse_variable: SourceForm.fields.inventory_variables.parseTypeName,
field_id: 'source_inventory_variables', onReady: waitStop });
@@ -1303,7 +1306,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name
}
if (sources_scope.source && (sources_scope.source.value === 'vmware' ||
- sources_scope.source.value === 'openstack')) {
+ sources_scope.source.value === 'openstack' ||
+ sources_scope.source.value === 'openstack_v3')) {
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.inventory_variables, true);
}
diff --git a/awx/ui/client/src/lists/HomeGroups.js b/awx/ui/client/src/lists/HomeGroups.js
index ad7180dff0..1c21fb6268 100644
--- a/awx/ui/client/src/lists/HomeGroups.js
+++ b/awx/ui/client/src/lists/HomeGroups.js
@@ -76,6 +76,9 @@ export default
},{
name: "OpenStack",
value: "openstack"
+ },{
+ name: "OpenStack V3",
+ value: "openstack_v3"
}],
sourceModel: 'inventory_source',
sourceField: 'source',
@@ -84,7 +87,7 @@ export default
has_external_source: {
label: 'Has external source?',
searchType: 'in',
- searchValue: 'ec2,rax,vmware,azure,gce,openstack',
+ searchValue: 'ec2,rax,vmware,azure,gce,openstack,openstack_v3',
searchOnly: true,
sourceModel: 'inventory_source',
sourceField: 'source'
diff --git a/awx/ui/client/src/lists/InventoryGroups.js b/awx/ui/client/src/lists/InventoryGroups.js
index 53881f3d7c..3b221e54e0 100644
--- a/awx/ui/client/src/lists/InventoryGroups.js
+++ b/awx/ui/client/src/lists/InventoryGroups.js
@@ -51,6 +51,9 @@ export default
},{
name: "OpenStack",
value: "openstack"
+ },{
+ name: "OpenStack V3",
+ value: "openstack_v3"
}],
sourceModel: 'inventory_source',
sourceField: 'source',
@@ -59,7 +62,7 @@ export default
has_external_source: {
label: 'Has external source?',
searchType: 'in',
- searchValue: 'ec2,rax,vmware,azure,gce,openstack',
+ searchValue: 'ec2,rax,vmware,azure,gce,openstack,openstack_v3',
searchOnly: true,
sourceModel: 'inventory_source',
sourceField: 'source'
From fcc02f7678dd47d6ece37ad6b86e3de872ab81c3 Mon Sep 17 00:00:00 2001
From: AlanCoding
Date: Fri, 18 Mar 2016 16:45:06 -0400
Subject: [PATCH 3/3] rebase and rename migrations corresponding to devel
change
---
...007_v300_changes.py => 0007_v300_credential_domain_field.py} | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename awx/main/migrations/{0007_v300_changes.py => 0007_v300_credential_domain_field.py} (88%)
diff --git a/awx/main/migrations/0007_v300_changes.py b/awx/main/migrations/0007_v300_credential_domain_field.py
similarity index 88%
rename from awx/main/migrations/0007_v300_changes.py
rename to awx/main/migrations/0007_v300_credential_domain_field.py
index f6d0ec1410..8875f9071f 100644
--- a/awx/main/migrations/0007_v300_changes.py
+++ b/awx/main/migrations/0007_v300_credential_domain_field.py
@@ -7,7 +7,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
- ('main', '0006_v300_changes'),
+ ('main', '0006_v300_create_system_job_templates'),
]
operations = [