Add --os-endpoint-type cli optional argument

User should be able to specify the endpoint type through
a CLI optional argument/ENV variable setting. We will name this new
optional argument: --os-endpoint-type (Env: OS_ENDPOINT_TYPE) and
based on the value given, the service API will use that specific
endpoint type. Possible values: public, admin, internal.

DocImpact
Closes-Bug: #1454392
Change-Id: Ife3d4e46b44c0ddcd712b1130e27e362545a9a29
This commit is contained in:
Roxana Gherle 2015-05-22 16:22:35 -07:00
parent 7e067c6f4f
commit 5521e4c504
15 changed files with 97 additions and 5 deletions

View File

@ -78,6 +78,7 @@ The keys match the :program:`openstack` global options but without the
username: openstack username: openstack
password: xyzpdq!lazydog password: xyzpdq!lazydog
region_name: DFW,ORD,IAD region_name: DFW,ORD,IAD
endpoint_type: internal
In the above example, the ``auth_url`` for the ``rackspace`` cloud is taken In the above example, the ``auth_url`` for the ``rackspace`` cloud is taken
from :file:`clouds-public.yaml` (see below). from :file:`clouds-public.yaml` (see below).
@ -96,6 +97,7 @@ to the following options if the ``rackspace`` entry in :file:`clouds-public.yaml
--os-username openstack --os-username openstack
--os-password xyzpdq!lazydog --os-password xyzpdq!lazydog
--os-region-name DFW --os-region-name DFW
--os-endpoint-type internal
and can be selected on the command line:: and can be selected on the command line::
@ -105,13 +107,17 @@ Note that multiple regions are listed in the ``rackspace`` entry. An otherwise
identical configuration is created for each region. If ``-os-region-name`` is not identical configuration is created for each region. If ``-os-region-name`` is not
specified on the command line, the first region in the list is used by default. specified on the command line, the first region in the list is used by default.
The selection of ``endpoint_type`` (as seen above in the ``rackspace`` entry)
is optional. For this configuration to work, every service for this cloud
instance must already be configured to support this type of endpoint.
clouds-public.yaml clouds-public.yaml
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
:file:`clouds-public.yaml` is a configuration file that is intended to contain :file:`clouds-public.yaml` is a configuration file that is intended to contain
public information about clouds that are common across a large number of users. public information about clouds that are common across a large number of users.
The idea is that :file:`clouds-public.yaml` could easily be shared among users The idea is that :file:`clouds-public.yaml` could easily be shared among users
to simplify public could configuration. to simplify public cloud configuration.
Similar to :file:`clouds.yaml`, OpenStackClient looks for Similar to :file:`clouds.yaml`, OpenStackClient looks for
:file:`clouds-public.yaml` in the following locations: :file:`clouds-public.yaml` in the following locations:

View File

@ -120,6 +120,8 @@ OPTIONS
:option:`--os-XXXX-api-version` <XXXX-api-version> :option:`--os-XXXX-api-version` <XXXX-api-version>
Additional API version options will be available depending on the installed API libraries. Additional API version options will be available depending on the installed API libraries.
:option:`--os-endpoint-type` <endpoint-type>
Endpoint type. Valid options are `public`, `admin` and `internal`.
COMMANDS COMMANDS
======== ========
@ -344,6 +346,9 @@ The following environment variables can be set to alter the behaviour of :progra
:envvar:`OS_XXXX_API_VERSION` :envvar:`OS_XXXX_API_VERSION`
Additional API version options will be available depending on the installed API libraries. Additional API version options will be available depending on the installed API libraries.
:envvar:`OS_ENDPOINT_TYPE`
Endpoint type. Valid options are `public`, `admin` and `internal`.
BUGS BUGS
==== ====

View File

@ -86,6 +86,7 @@ class ClientManager(object):
self._pw_callback = pw_func self._pw_callback = pw_func
self._url = self._cli_options.auth.get('url', None) self._url = self._cli_options.auth.get('url', None)
self._region_name = self._cli_options.region_name self._region_name = self._cli_options.region_name
self._endpoint_type = self._cli_options.endpoint_type
self.timing = self._cli_options.timing self.timing = self._cli_options.timing
@ -181,18 +182,23 @@ class ClientManager(object):
self._auth_ref = self.auth.get_auth_ref(self.session) self._auth_ref = self.auth.get_auth_ref(self.session)
return self._auth_ref return self._auth_ref
def get_endpoint_for_service_type(self, service_type, region_name=None): def get_endpoint_for_service_type(self, service_type, region_name=None,
endpoint_type='public'):
"""Return the endpoint URL for the service type.""" """Return the endpoint URL for the service type."""
if not endpoint_type:
endpoint_type = 'public'
# See if we are using password flow auth, i.e. we have a # See if we are using password flow auth, i.e. we have a
# service catalog to select endpoints from # service catalog to select endpoints from
if self.auth_ref: if self.auth_ref:
endpoint = self.auth_ref.service_catalog.url_for( endpoint = self.auth_ref.service_catalog.url_for(
service_type=service_type, service_type=service_type,
region_name=region_name, region_name=region_name,
endpoint_type=endpoint_type,
) )
else: else:
# Get the passed endpoint directly from the auth plugin # Get the passed endpoint directly from the auth plugin
endpoint = self.auth.get_endpoint(self.session) endpoint = self.auth.get_endpoint(self.session,
interface=endpoint_type)
return endpoint return endpoint

View File

@ -368,3 +368,11 @@ def read_blob_file_contents(blob_file):
except IOError: except IOError:
msg = "Error occurred trying to read from file %s" msg = "Error occurred trying to read from file %s"
raise exceptions.CommandError(msg % blob_file) raise exceptions.CommandError(msg % blob_file)
def build_kwargs_dict(arg_name, value):
"""Return a dictionary containing `arg_name` if `value` is set."""
kwargs = {}
if value:
kwargs[arg_name] = value
return kwargs

View File

@ -48,12 +48,17 @@ def make_client(instance):
extensions = [extension.Extension('list_extensions', list_extensions)] extensions = [extension.Extension('list_extensions', list_extensions)]
# Remember endpoint_type only if it is set
kwargs = utils.build_kwargs_dict('endpoint_type',
instance._endpoint_type)
client = compute_client( client = compute_client(
session=instance.session, session=instance.session,
extensions=extensions, extensions=extensions,
http_log_debug=http_log_debug, http_log_debug=http_log_debug,
timings=instance.timing, timings=instance.timing,
region_name=instance._region_name, region_name=instance._region_name,
**kwargs
) )
return client return client

View File

@ -46,10 +46,15 @@ def make_client(instance):
API_VERSIONS) API_VERSIONS)
LOG.debug('Instantiating identity client: %s', identity_client) LOG.debug('Instantiating identity client: %s', identity_client)
# Remember interface only if endpoint_type is set
kwargs = utils.build_kwargs_dict('interface',
instance._endpoint_type)
client = identity_client( client = identity_client(
session=instance.session, session=instance.session,
region_name=instance._region_name, region_name=instance._region_name,
) **kwargs
)
return client return client

View File

@ -46,6 +46,7 @@ def make_client(instance):
endpoint = instance.get_endpoint_for_service_type( endpoint = instance.get_endpoint_for_service_type(
API_NAME, API_NAME,
region_name=instance._region_name, region_name=instance._region_name,
endpoint_type=instance._endpoint_type,
) )
client = image_client( client = image_client(
@ -68,6 +69,7 @@ def make_client(instance):
endpoint=instance.get_endpoint_for_service_type( endpoint=instance.get_endpoint_for_service_type(
IMAGE_API_TYPE, IMAGE_API_TYPE,
region_name=instance._region_name, region_name=instance._region_name,
endpoint_type=instance._endpoint_type,
) )
) )

View File

@ -47,11 +47,17 @@ def make_client(instance):
endpoint = instance.get_endpoint_for_service_type( endpoint = instance.get_endpoint_for_service_type(
API_NAME, API_NAME,
region_name=instance._region_name, region_name=instance._region_name,
endpoint_type=instance._endpoint_type,
) )
# Remember endpoint_type only if it is set
kwargs = utils.build_kwargs_dict('endpoint_type',
instance._endpoint_type)
client = network_client( client = network_client(
session=instance.session, session=instance.session,
region_name=instance._region_name, region_name=instance._region_name,
**kwargs
) )
network_api = utils.get_client_class( network_api = utils.get_client_class(

View File

@ -36,6 +36,7 @@ def make_client(instance):
endpoint = instance.get_endpoint_for_service_type( endpoint = instance.get_endpoint_for_service_type(
'object-store', 'object-store',
region_name=instance._region_name, region_name=instance._region_name,
endpoint_type=instance._endpoint_type,
) )
client = object_store_v1.APIv1( client = object_store_v1.APIv1(

View File

@ -208,6 +208,15 @@ class OpenStackShell(app.App):
help='Default domain ID, default=' + help='Default domain ID, default=' +
DEFAULT_DOMAIN + DEFAULT_DOMAIN +
' (Env: OS_DEFAULT_DOMAIN)') ' (Env: OS_DEFAULT_DOMAIN)')
parser.add_argument(
'--os-endpoint-type',
metavar='<endpoint-type>',
dest='endpoint_type',
choices=['admin', 'public', 'internal'],
default=utils.env('OS_ENDPOINT_TYPE'),
help='Select an endpoint type.'
' Valid endpoint types: [admin, public, internal].'
' (Env: OS_ENDPOINT_TYPE)')
parser.add_argument( parser.add_argument(
'--timing', '--timing',
default=False, default=False,
@ -254,7 +263,10 @@ class OpenStackShell(app.App):
self.options.project_name = tenant_name self.options.project_name = tenant_name
# Do configuration file handling # Do configuration file handling
cc = cloud_config.OpenStackConfig() # Ignore the default value of endpoint_type. Only if it is set later
# will it be used.
cc = cloud_config.OpenStackConfig(
override_defaults={'endpoint_type': None, })
self.log.debug("defaults: %s", cc.defaults) self.log.debug("defaults: %s", cc.defaults)
self.cloud = cc.get_one_cloud( self.cloud = cc.get_one_cloud(

View File

@ -54,6 +54,7 @@ class FakeOptions(object):
self.identity_api_version = '2.0' self.identity_api_version = '2.0'
self.timing = None self.timing = None
self.region_name = None self.region_name = None
self.endpoint_type = None
self.url = None self.url = None
self.auth = {} self.auth = {}
self.default_domain = 'default' self.default_domain = 'default'
@ -123,6 +124,8 @@ class TestClientManager(utils.TestCase):
auth_url=fakes.AUTH_URL, auth_url=fakes.AUTH_URL,
), ),
auth_type='v2token', auth_type='v2token',
endpoint_type=fakes.ENDPOINT_TYPE,
region_name=fakes.REGION_NAME,
), ),
api_version=API_VERSION, api_version=API_VERSION,
verify=True verify=True
@ -137,6 +140,14 @@ class TestClientManager(utils.TestCase):
client_manager.auth, client_manager.auth,
auth_v2.Token, auth_v2.Token,
) )
self.assertEqual(
fakes.ENDPOINT_TYPE,
client_manager._endpoint_type,
)
self.assertEqual(
fakes.REGION_NAME,
client_manager._region_name,
)
self.assertFalse(client_manager._insecure) self.assertFalse(client_manager._insecure)
self.assertTrue(client_manager._verify) self.assertTrue(client_manager._verify)

View File

@ -159,6 +159,16 @@ class TestUtils(test_utils.TestCase):
self.assertFalse(utils.wait_for_delete(manager, res_id)) self.assertFalse(utils.wait_for_delete(manager, res_id))
self.assertFalse(mock_sleep.called) self.assertFalse(mock_sleep.called)
def test_build_kwargs_dict_value_set(self):
self.assertEqual({'arg_bla': 'bla'},
utils.build_kwargs_dict('arg_bla', 'bla'))
def test_build_kwargs_dict_value_None(self):
self.assertEqual({}, utils.build_kwargs_dict('arg_bla', None))
def test_build_kwargs_dict_value_empty_str(self):
self.assertEqual({}, utils.build_kwargs_dict('arg_bla', ''))
class NoUniqueMatch(Exception): class NoUniqueMatch(Exception):
pass pass

View File

@ -26,6 +26,8 @@ AUTH_URL = "http://0.0.0.0"
USERNAME = "itchy" USERNAME = "itchy"
PASSWORD = "scratchy" PASSWORD = "scratchy"
PROJECT_NAME = "poochie" PROJECT_NAME = "poochie"
REGION_NAME = "richie"
ENDPOINT_TYPE = "catchy"
TEST_RESPONSE_DICT = fixture.V2Token(token_id=AUTH_TOKEN, TEST_RESPONSE_DICT = fixture.V2Token(token_id=AUTH_TOKEN,
user_name=USERNAME) user_name=USERNAME)

View File

@ -38,6 +38,7 @@ DEFAULT_REGION_NAME = "ZZ9_Plural_Z_Alpha"
DEFAULT_TOKEN = "token" DEFAULT_TOKEN = "token"
DEFAULT_SERVICE_URL = "http://127.0.0.1:8771/v3.0/" DEFAULT_SERVICE_URL = "http://127.0.0.1:8771/v3.0/"
DEFAULT_AUTH_PLUGIN = "v2password" DEFAULT_AUTH_PLUGIN = "v2password"
DEFAULT_ENDPOINT_TYPE = "internal"
DEFAULT_COMPUTE_API_VERSION = "2" DEFAULT_COMPUTE_API_VERSION = "2"
DEFAULT_IDENTITY_API_VERSION = "2" DEFAULT_IDENTITY_API_VERSION = "2"
@ -61,6 +62,7 @@ CLOUD_1 = {
}, },
'region_name': 'occ-cloud', 'region_name': 'occ-cloud',
'donut': 'glazed', 'donut': 'glazed',
'endpoint_type': 'public',
} }
} }
} }
@ -104,6 +106,7 @@ global_options = {
'--os-default-domain': (DEFAULT_DOMAIN_NAME, True, True), '--os-default-domain': (DEFAULT_DOMAIN_NAME, True, True),
'--os-cacert': ('/dev/null', True, True), '--os-cacert': ('/dev/null', True, True),
'--timing': (True, True, False), '--timing': (True, True, False),
'--os-endpoint-type': (DEFAULT_ENDPOINT_TYPE, True, True)
} }
auth_options = { auth_options = {
@ -123,6 +126,7 @@ auth_options = {
'--os-auth-type': ("v2password", True, True), '--os-auth-type': ("v2password", True, True),
'--os-token': (DEFAULT_TOKEN, True, True), '--os-token': (DEFAULT_TOKEN, True, True),
'--os-url': (DEFAULT_SERVICE_URL, True, True), '--os-url': (DEFAULT_SERVICE_URL, True, True),
'--os-endpoint-type': (DEFAULT_ENDPOINT_TYPE, True, True),
} }
@ -608,6 +612,10 @@ class TestShellCli(TestShell):
'glazed', 'glazed',
_shell.cloud.config['donut'], _shell.cloud.config['donut'],
) )
self.assertEqual(
'public',
_shell.cloud.config['endpoint_type'],
)
@mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file") @mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file")
@mock.patch("os_client_config.config.OpenStackConfig._load_config_file") @mock.patch("os_client_config.config.OpenStackConfig._load_config_file")

View File

@ -53,11 +53,16 @@ def make_client(instance):
extensions = [extension.Extension('list_extensions', list_extensions)] extensions = [extension.Extension('list_extensions', list_extensions)]
# Remember endpoint_type only if it is set
kwargs = utils.build_kwargs_dict('endpoint_type',
instance._endpoint_type)
client = volume_client( client = volume_client(
session=instance.session, session=instance.session,
extensions=extensions, extensions=extensions,
http_log_debug=http_log_debug, http_log_debug=http_log_debug,
region_name=instance._region_name, region_name=instance._region_name,
**kwargs
) )
return client return client