diff --git a/awx/locale/django.pot b/awx/locale/django.pot index 3d2cf41999..e5fbe05390 100644 --- a/awx/locale/django.pot +++ b/awx/locale/django.pot @@ -3354,6 +3354,15 @@ msgid "" "common scenarios." msgstr "" +#: awx/main/models/credential/__init__.py:824 +msgid "Region Name" +msgstr "" + +#: awx/main/models/credential/__init__.py:826 +msgid "" +"For some cloud providers, like OVH, region must be specified." +msgstr "" + #: awx/main/models/credential/__init__.py:824 #: awx/main/models/credential/__init__.py:1131 #: awx/main/models/credential/__init__.py:1166 diff --git a/awx/locale/en-us/LC_MESSAGES/django.po b/awx/locale/en-us/LC_MESSAGES/django.po index 3d2cf41999..e5fbe05390 100644 --- a/awx/locale/en-us/LC_MESSAGES/django.po +++ b/awx/locale/en-us/LC_MESSAGES/django.po @@ -3354,6 +3354,15 @@ msgid "" "common scenarios." msgstr "" +#: awx/main/models/credential/__init__.py:824 +msgid "Region Name" +msgstr "" + +#: awx/main/models/credential/__init__.py:826 +msgid "" +"For some cloud providers, like OVH, region must be specified." +msgstr "" + #: awx/main/models/credential/__init__.py:824 #: awx/main/models/credential/__init__.py:1131 #: awx/main/models/credential/__init__.py:1166 diff --git a/awx/locale/fr/LC_MESSAGES/django.po b/awx/locale/fr/LC_MESSAGES/django.po index 62c2ba7292..bcb54c548b 100644 --- a/awx/locale/fr/LC_MESSAGES/django.po +++ b/awx/locale/fr/LC_MESSAGES/django.po @@ -3294,6 +3294,16 @@ msgid "" "common scenarios." msgstr "Les domaines OpenStack définissent les limites administratives. Ils sont nécessaires uniquement pour les URL d’authentification Keystone v3. Voir la documentation Ansible Tower pour les scénarios courants." +#: awx/main/models/credential/__init__.py:824 +msgid "Region Name" +msgstr "Nom de la region" + +#: awx/main/models/credential/__init__.py:826 +msgid "" +"For some cloud providers, like OVH, region must be specified." +msgstr "" +"Chez certains fournisseurs, comme OVH, vous devez spécifier le nom de la région" + #: awx/main/models/credential/__init__.py:812 #: awx/main/models/credential/__init__.py:1110 #: awx/main/models/credential/__init__.py:1144 diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index 66db962430..e8a2884083 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -819,6 +819,11 @@ ManagedCredentialType( 'It is only needed for Keystone v3 authentication ' 'URLs. Refer to Ansible Tower documentation for ' 'common scenarios.') + }, { + 'id': 'region', + 'label': ugettext_noop('Region Name'), + 'type': 'string', + 'help_text': ugettext_noop('For some cloud providers, like OVH, region must be specified'), }, { 'id': 'verify_ssl', 'label': ugettext_noop('Verify SSL'), diff --git a/awx/main/models/credential/injectors.py b/awx/main/models/credential/injectors.py index 75d1f17bfe..ef30b91945 100644 --- a/awx/main/models/credential/injectors.py +++ b/awx/main/models/credential/injectors.py @@ -82,6 +82,7 @@ def _openstack_data(cred): if cred.has_input('domain'): openstack_auth['domain_name'] = cred.get_input('domain', default='') verify_state = cred.get_input('verify_ssl', default=True) + openstack_data = { 'clouds': { 'devstack': { @@ -90,6 +91,10 @@ def _openstack_data(cred): }, }, } + + if cred.has_input('project_region_name'): + openstack_data['clouds']['devstack']['region_name'] = cred.get_input('project_region_name', default='') + return openstack_data diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index b1c7765328..166ea95f19 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -246,6 +246,53 @@ def test_openstack_client_config_generation_with_project_domain_name(mocker, sou } +@pytest.mark.parametrize("source,expected", [ + (None, True), (False, False), (True, True) +]) +def test_openstack_client_config_generation_with_project_region_name(mocker, source, expected, private_data_dir): + update = tasks.RunInventoryUpdate() + credential_type = CredentialType.defaults['openstack']() + inputs = { + 'host': 'https://keystone.openstack.example.org', + 'username': 'demo', + 'password': 'secrete', + 'project': 'demo-project', + 'domain': 'my-demo-domain', + 'project_domain_name': 'project-domain', + 'project_region_name': 'region-name', + } + if source is not None: + inputs['verify_ssl'] = source + credential = Credential(pk=1, credential_type=credential_type, inputs=inputs) + + inventory_update = mocker.Mock(**{ + 'source': 'openstack', + 'source_vars_dict': {}, + 'get_cloud_credential': mocker.Mock(return_value=credential), + 'get_extra_credentials': lambda x: [], + 'ansible_virtualenv_path': '/venv/foo' + }) + cloud_config = update.build_private_data(inventory_update, private_data_dir) + cloud_credential = yaml.safe_load( + cloud_config.get('credentials')[credential] + ) + assert cloud_credential['clouds'] == { + 'devstack': { + 'auth': { + 'auth_url': 'https://keystone.openstack.example.org', + 'password': 'secrete', + 'project_name': 'demo-project', + 'username': 'demo', + 'domain_name': 'my-demo-domain', + 'project_domain_name': 'project-domain', + }, + 'verify': expected, + 'private': True, + 'region_name': 'region-name', + } + } + + @pytest.mark.parametrize("source,expected", [ (False, False), (True, True) ]) diff --git a/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json b/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json index b7b3189951..6281f15024 100644 --- a/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json +++ b/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json @@ -275,6 +275,11 @@ "type": "string", "help_text": "OpenStack domains define administrative boundaries. It is only needed for Keystone v3 authentication URLs. Refer to Ansible Tower documentation for common scenarios." }, + { + "id": "project_region_name", + "label": "Region Name", + "type": "string" + }, { "id": "verify_ssl", "label": "Verify SSL",