Get auth from context for glance endpoint
Change the Adapter loading for glance to use the auth from the user
context instead of exposing and requiring it in the conf. With this
change, it is possible to leave the [glance] conf section empty and
still be able to discover the image API endpoint from the service
catalog.
Note that, when we do this, we often end up with the user auth being a
_ContextAuthPlugin, which doesn't conform to the characteristics of
keystoneauth1.identity.base.BaseIdentityPlugin as augmented in
keystoneauth1 3.1.0. This requires a series of workarounds until bug
1709118 is fixed. These, along with workarounds for bugs 1707993 and
1707995, are subsumed with this change set in a (hopefully temporary)
helper method nova.utils.get_endpoint.
This lays the foundation for other services that should use user
context for authentication - those via which Nova is acting on behalf
of the user, i.e. cinder, keystone, and (sometimes) neutron[1].
(Services such as placement and ironic (and sometimes neutron) should
continue to use admin auth context loaded from the conf.)
[1] bb4faf40df/nova/network/neutronv2/api.py (L149-L160)
Co-Authored-By: Eric Fried <efried@us.ibm.com>
Partial-Implements: bp use-ksa-adapter-for-endpoints
Change-Id: I4e755b9c66ec8bc3af0393e81cffd91c56064717
This commit is contained in:
parent
bb4faf40df
commit
9519601401
nova
api/openstack/compute
compute
conf
context.pyimage
notifications
tests/unit
utils.pyvirt/xenapi/image
releasenotes/notes
@ -1023,7 +1023,7 @@ class ServersController(wsgi.Controller):
|
||||
|
||||
# build location of newly-created image entity
|
||||
image_id = str(image['id'])
|
||||
image_ref = glance.generate_image_url(image_id)
|
||||
image_ref = glance.generate_image_url(image_id, context)
|
||||
|
||||
resp = webob.Response(status_int=202)
|
||||
resp.headers['Location'] = image_ref
|
||||
|
@ -132,7 +132,8 @@ class ViewBuilder(common.ViewBuilder):
|
||||
|
||||
def _get_alternate_link(self, request, identifier):
|
||||
"""Create an alternate link for a specific image id."""
|
||||
glance_url = glance.generate_glance_url()
|
||||
glance_url = glance.generate_glance_url(
|
||||
request.environ['nova.context'])
|
||||
glance_url = self._update_glance_link_prefix(glance_url)
|
||||
return '/'.join([glance_url,
|
||||
self._collection_name,
|
||||
|
@ -2946,7 +2946,7 @@ class ComputeManager(manager.Manager):
|
||||
# image_ref, not the new one. Since the DB has been updated
|
||||
# to point to the new one... we have to override it.
|
||||
# TODO(jaypipes): Move generate_image_url() into the nova.image.api
|
||||
orig_image_ref_url = glance.generate_image_url(orig_image_ref)
|
||||
orig_image_ref_url = glance.generate_image_url(orig_image_ref, context)
|
||||
extra_usage_info = {'image_ref_url': orig_image_ref_url}
|
||||
compute_utils.notify_usage_exists(
|
||||
self.notifier, context, instance,
|
||||
|
@ -149,17 +149,15 @@ def register_opts(conf):
|
||||
conf.register_group(glance_group)
|
||||
conf.register_opts(glance_opts, group=glance_group)
|
||||
|
||||
confutils.register_ksa_opts(conf, glance_group, DEFAULT_SERVICE_TYPE,
|
||||
deprecated_opts=deprecated_ksa_opts)
|
||||
confutils.register_ksa_opts(
|
||||
conf, glance_group, DEFAULT_SERVICE_TYPE, include_auth=False,
|
||||
deprecated_opts=deprecated_ksa_opts)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return {glance_group: (
|
||||
glance_opts +
|
||||
ks_loading.get_session_conf_options() +
|
||||
ks_loading.get_auth_plugin_conf_options('password') +
|
||||
ks_loading.get_auth_plugin_conf_options('v2password') +
|
||||
ks_loading.get_auth_plugin_conf_options('v3password') +
|
||||
confutils.get_ksa_adapter_opts(DEFAULT_SERVICE_TYPE,
|
||||
deprecated_opts=deprecated_ksa_opts))
|
||||
}
|
||||
|
@ -55,7 +55,8 @@ def _dummy_opt(name):
|
||||
return cfg.Opt(name, type=lambda x: None)
|
||||
|
||||
|
||||
def register_ksa_opts(conf, group, default_service_type, deprecated_opts=None):
|
||||
def register_ksa_opts(conf, group, default_service_type, include_auth=True,
|
||||
deprecated_opts=None):
|
||||
"""Register keystoneauth auth, Session, and Adapter opts.
|
||||
|
||||
:param conf: oslo_config.cfg.CONF in which to register the options
|
||||
@ -63,6 +64,10 @@ def register_ksa_opts(conf, group, default_service_type, deprecated_opts=None):
|
||||
options.
|
||||
:param default_service_type: Default for the service_type conf option on
|
||||
the Adapter.
|
||||
:param include_auth: For service types where Nova is acting on behalf of
|
||||
the user, auth should come from the user context.
|
||||
In those cases, set this arg to False to avoid
|
||||
registering ksa auth options.
|
||||
:param deprecated_opts: dict of deprecated opts to register with the ksa
|
||||
Session or Adapter opts. See docstring for
|
||||
the deprecated_opts param of:
|
||||
@ -72,7 +77,8 @@ def register_ksa_opts(conf, group, default_service_type, deprecated_opts=None):
|
||||
group = getattr(group, 'name', group)
|
||||
ks_loading.register_session_conf_options(
|
||||
conf, group, deprecated_opts=deprecated_opts)
|
||||
ks_loading.register_auth_conf_options(conf, group)
|
||||
if include_auth:
|
||||
ks_loading.register_auth_conf_options(conf, group)
|
||||
conf.register_opts(get_ksa_adapter_opts(
|
||||
default_service_type, deprecated_opts=deprecated_opts), group=group)
|
||||
# Have to register dummies for the version-related opts we removed
|
||||
|
@ -118,7 +118,7 @@ class RequestContext(context.RequestContext):
|
||||
if service_catalog:
|
||||
# Only include required parts of service_catalog
|
||||
self.service_catalog = [s for s in service_catalog
|
||||
if s.get('type') in ('block-storage', 'volumev3',
|
||||
if s.get('type') in ('image', 'block-storage', 'volumev3',
|
||||
'key-manager', 'placement')]
|
||||
else:
|
||||
# if list is empty or none
|
||||
|
@ -56,7 +56,8 @@ CONF = nova.conf.CONF
|
||||
_SESSION = None
|
||||
|
||||
|
||||
def _glanceclient_from_endpoint(context, endpoint, version):
|
||||
def _session_and_auth(context):
|
||||
# Session is cached, but auth needs to be pulled from context each time.
|
||||
global _SESSION
|
||||
|
||||
if not _SESSION:
|
||||
@ -65,21 +66,25 @@ def _glanceclient_from_endpoint(context, endpoint, version):
|
||||
|
||||
auth = service_auth.get_auth_plugin(context)
|
||||
|
||||
# TODO(johngarbutt) eventually we should default to getting the
|
||||
# endpoint URL from the service catalog.
|
||||
return glanceclient.Client(version, session=_SESSION, auth=auth,
|
||||
return _SESSION, auth
|
||||
|
||||
|
||||
def _glanceclient_from_endpoint(context, endpoint, version):
|
||||
sess, auth = _session_and_auth(context)
|
||||
|
||||
return glanceclient.Client(version, session=sess, auth=auth,
|
||||
endpoint_override=endpoint,
|
||||
global_request_id=context.global_id)
|
||||
|
||||
|
||||
def generate_glance_url():
|
||||
def generate_glance_url(context):
|
||||
"""Return a random glance url from the api servers we know about."""
|
||||
return next(get_api_servers())
|
||||
return next(get_api_servers(context))
|
||||
|
||||
|
||||
def generate_image_url(image_ref):
|
||||
def generate_image_url(image_ref, context):
|
||||
"""Generate an image URL from an image_ref."""
|
||||
return "%s/images/%s" % (generate_glance_url(), image_ref)
|
||||
return "%s/images/%s" % (generate_glance_url(context), image_ref)
|
||||
|
||||
|
||||
def _endpoint_from_image_ref(image_href):
|
||||
@ -106,7 +111,7 @@ def generate_identity_headers(context, status='Confirmed'):
|
||||
}
|
||||
|
||||
|
||||
def get_api_servers():
|
||||
def get_api_servers(context):
|
||||
"""Shuffle a list of service endpoints and return an iterator that will
|
||||
cycle through the list, looping around to the beginning if necessary.
|
||||
"""
|
||||
@ -117,13 +122,12 @@ def get_api_servers():
|
||||
api_servers = CONF.glance.api_servers
|
||||
random.shuffle(api_servers)
|
||||
else:
|
||||
# TODO(efried): Plumb in a reasonable auth from callers' contexts
|
||||
sess, auth = _session_and_auth(context)
|
||||
ksa_adap = utils.get_ksa_adapter(
|
||||
nova.conf.glance.DEFAULT_SERVICE_TYPE,
|
||||
ksa_auth=auth, ksa_session=sess,
|
||||
min_version='2.0', max_version='2.latest')
|
||||
# TODO(efried): Use ksa_adap.get_endpoint() when bug #1707995 is fixed.
|
||||
api_servers = [ksa_adap.endpoint_override or
|
||||
ksa_adap.get_endpoint_data().catalog_url]
|
||||
api_servers = [utils.get_endpoint(ksa_adap)]
|
||||
|
||||
return itertools.cycle(api_servers)
|
||||
|
||||
@ -149,7 +153,7 @@ class GlanceClientWrapper(object):
|
||||
def _create_onetime_client(self, context, version):
|
||||
"""Create a client that will be used for one call."""
|
||||
if self.api_servers is None:
|
||||
self.api_servers = get_api_servers()
|
||||
self.api_servers = get_api_servers(context)
|
||||
self.api_server = next(self.api_servers)
|
||||
return _glanceclient_from_endpoint(context, self.api_server, version)
|
||||
|
||||
|
@ -394,7 +394,7 @@ def info_from_instance(context, instance, network_info,
|
||||
modifications.
|
||||
|
||||
"""
|
||||
image_ref_url = glance.generate_image_url(instance.image_ref)
|
||||
image_ref_url = glance.generate_image_url(instance.image_ref, context)
|
||||
|
||||
instance_type = instance.get_flavor()
|
||||
instance_type_name = instance_type.get('name', '')
|
||||
|
@ -92,7 +92,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
"rel": "alternate",
|
||||
"type": "application/vnd.openstack.image",
|
||||
"href": self.alternate %
|
||||
(glance.generate_glance_url(),
|
||||
(glance.generate_glance_url('ctx'),
|
||||
123),
|
||||
}],
|
||||
},
|
||||
@ -136,7 +136,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
"type":
|
||||
"application/vnd.openstack.image",
|
||||
"href": self.alternate %
|
||||
(glance.generate_glance_url(),
|
||||
(glance.generate_glance_url('ctx'),
|
||||
124),
|
||||
}],
|
||||
},
|
||||
@ -196,7 +196,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
image_125["links"][0]["href"] = "%s/125" % self.url_prefix
|
||||
image_125["links"][1]["href"] = "%s/125" % self.bookmark_prefix
|
||||
image_125["links"][2]["href"] = (
|
||||
"%s/images/125" % glance.generate_glance_url())
|
||||
"%s/images/125" % glance.generate_glance_url('ctx'))
|
||||
|
||||
image_126 = copy.deepcopy(self.expected_image_124["image"])
|
||||
image_126['id'] = '126'
|
||||
@ -206,7 +206,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
image_126["links"][0]["href"] = "%s/126" % self.url_prefix
|
||||
image_126["links"][1]["href"] = "%s/126" % self.bookmark_prefix
|
||||
image_126["links"][2]["href"] = (
|
||||
"%s/images/126" % glance.generate_glance_url())
|
||||
"%s/images/126" % glance.generate_glance_url('ctx'))
|
||||
|
||||
image_127 = copy.deepcopy(self.expected_image_124["image"])
|
||||
image_127['id'] = '127'
|
||||
@ -216,7 +216,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
image_127["links"][0]["href"] = "%s/127" % self.url_prefix
|
||||
image_127["links"][1]["href"] = "%s/127" % self.bookmark_prefix
|
||||
image_127["links"][2]["href"] = (
|
||||
"%s/images/127" % glance.generate_glance_url())
|
||||
"%s/images/127" % glance.generate_glance_url('ctx'))
|
||||
|
||||
image_128 = copy.deepcopy(self.expected_image_124["image"])
|
||||
image_128['id'] = '128'
|
||||
@ -226,7 +226,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
image_128["links"][0]["href"] = "%s/128" % self.url_prefix
|
||||
image_128["links"][1]["href"] = "%s/128" % self.bookmark_prefix
|
||||
image_128["links"][2]["href"] = (
|
||||
"%s/images/128" % glance.generate_glance_url())
|
||||
"%s/images/128" % glance.generate_glance_url('ctx'))
|
||||
|
||||
image_129 = copy.deepcopy(self.expected_image_124["image"])
|
||||
image_129['id'] = '129'
|
||||
@ -236,7 +236,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
image_129["links"][0]["href"] = "%s/129" % self.url_prefix
|
||||
image_129["links"][1]["href"] = "%s/129" % self.bookmark_prefix
|
||||
image_129["links"][2]["href"] = (
|
||||
"%s/images/129" % glance.generate_glance_url())
|
||||
"%s/images/129" % glance.generate_glance_url('ctx'))
|
||||
|
||||
image_130 = copy.deepcopy(self.expected_image_123["image"])
|
||||
image_130['id'] = '130'
|
||||
@ -247,7 +247,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
image_130["links"][0]["href"] = "%s/130" % self.url_prefix
|
||||
image_130["links"][1]["href"] = "%s/130" % self.bookmark_prefix
|
||||
image_130["links"][2]["href"] = (
|
||||
"%s/images/130" % glance.generate_glance_url())
|
||||
"%s/images/130" % glance.generate_glance_url('ctx'))
|
||||
|
||||
image_131 = copy.deepcopy(self.expected_image_123["image"])
|
||||
image_131['id'] = '131'
|
||||
@ -258,7 +258,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
image_131["links"][0]["href"] = "%s/131" % self.url_prefix
|
||||
image_131["links"][1]["href"] = "%s/131" % self.bookmark_prefix
|
||||
image_131["links"][2]["href"] = (
|
||||
"%s/images/131" % glance.generate_glance_url())
|
||||
"%s/images/131" % glance.generate_glance_url('ctx'))
|
||||
|
||||
expected = [self.expected_image_123["image"],
|
||||
self.expected_image_124["image"],
|
||||
@ -352,7 +352,7 @@ class ImagesControllerTestV21(test.NoDBTestCase):
|
||||
view = images_view.ViewBuilder()
|
||||
request = self.http_request.blank(self.url_base + 'images/1')
|
||||
generated_url = view._get_alternate_link(request, 1)
|
||||
actual_url = "%s/images/1" % glance.generate_glance_url()
|
||||
actual_url = "%s/images/1" % glance.generate_glance_url('ctx')
|
||||
self.assertEqual(generated_url, actual_url)
|
||||
|
||||
def _check_response(self, controller_method, response, expected_code):
|
||||
|
@ -896,7 +896,7 @@ class ServerActionsControllerTestV21(test.TestCase):
|
||||
|
||||
location = response.headers['Location']
|
||||
self.assertEqual(self.image_url + '123' if self.image_url else
|
||||
glance.generate_image_url('123'),
|
||||
glance.generate_image_url('123', self.context),
|
||||
location)
|
||||
|
||||
def test_create_image_v2_45(self):
|
||||
@ -997,8 +997,9 @@ class ServerActionsControllerTestV21(test.TestCase):
|
||||
FAKE_UUID, body=body)
|
||||
|
||||
location = response.headers['Location']
|
||||
image_id = location.replace(self.image_url or
|
||||
glance.generate_image_url(''), '')
|
||||
image_id = location.replace(
|
||||
self.image_url or glance.generate_image_url('', self.context),
|
||||
'')
|
||||
image = image_service.show(None, image_id)
|
||||
|
||||
self.assertEqual(image['name'], 'snapshot_of_volume_backed')
|
||||
@ -1145,7 +1146,8 @@ class ServerActionsControllerTestV21(test.TestCase):
|
||||
|
||||
location = response.headers['Location']
|
||||
self.assertEqual(self.image_url + '123' if self.image_url else
|
||||
glance.generate_image_url('123'), location)
|
||||
glance.generate_image_url('123', self.context),
|
||||
location)
|
||||
|
||||
def test_create_image_with_too_much_metadata(self):
|
||||
body = {
|
||||
|
@ -2362,7 +2362,8 @@ class ComputeTestCase(BaseTestCase,
|
||||
self.assertIn('display_name', payload)
|
||||
self.assertIn('created_at', payload)
|
||||
self.assertIn('launched_at', payload)
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF)
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF,
|
||||
self.context)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
msg = fake_notifier.NOTIFICATIONS[0]
|
||||
self.assertIn('rescue_image_name', msg.payload)
|
||||
@ -2402,7 +2403,8 @@ class ComputeTestCase(BaseTestCase,
|
||||
self.assertIn('display_name', payload)
|
||||
self.assertIn('created_at', payload)
|
||||
self.assertIn('launched_at', payload)
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF)
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF,
|
||||
self.context)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
|
||||
self.compute.terminate_instance(self.context, instance, [], [])
|
||||
@ -4149,7 +4151,7 @@ class ComputeTestCase(BaseTestCase,
|
||||
self.assertIn('launched_at', payload)
|
||||
self.assertIn('fixed_ips', payload)
|
||||
self.assertTrue(payload['launched_at'])
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF)
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF, self.context)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
self.assertEqual('Success', payload['message'])
|
||||
self.compute.terminate_instance(self.context, instance, [], [])
|
||||
@ -4279,7 +4281,7 @@ class ComputeTestCase(BaseTestCase,
|
||||
self.assertIn('deleted_at', payload)
|
||||
self.assertEqual(payload['terminated_at'], utils.strtime(cur_time))
|
||||
self.assertEqual(payload['deleted_at'], utils.strtime(cur_time))
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF)
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF, self.context)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
|
||||
@mock.patch.object(fake.FakeDriver, "macs_for_instance")
|
||||
@ -4968,8 +4970,9 @@ class ComputeTestCase(BaseTestCase,
|
||||
|
||||
inst_ref.refresh()
|
||||
|
||||
image_ref_url = glance.generate_image_url(image_ref)
|
||||
new_image_ref_url = glance.generate_image_url(new_image_ref)
|
||||
image_ref_url = glance.generate_image_url(image_ref, self.context)
|
||||
new_image_ref_url = glance.generate_image_url(new_image_ref,
|
||||
self.context)
|
||||
|
||||
self.assertEqual(len(fake_notifier.NOTIFICATIONS), 3)
|
||||
msg = fake_notifier.NOTIFICATIONS[0]
|
||||
@ -5057,7 +5060,8 @@ class ComputeTestCase(BaseTestCase,
|
||||
self.assertIn('created_at', payload)
|
||||
self.assertIn('launched_at', payload)
|
||||
self.assertEqual(payload['launched_at'], utils.strtime(cur_time))
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF)
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF,
|
||||
self.context)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
self.compute.terminate_instance(self.context, instance, [], [])
|
||||
|
||||
@ -5109,7 +5113,7 @@ class ComputeTestCase(BaseTestCase,
|
||||
self.assertIn('display_name', payload)
|
||||
self.assertIn('created_at', payload)
|
||||
self.assertIn('launched_at', payload)
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF)
|
||||
image_ref_url = glance.generate_image_url(FAKE_IMAGE_REF, self.context)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
self.compute.terminate_instance(self.context, instance, [], [])
|
||||
|
||||
|
@ -449,8 +449,8 @@ class UsageInfoTestCase(test.TestCase):
|
||||
"Key %s not in payload" % attr)
|
||||
self.assertEqual(payload['image_meta'],
|
||||
{'md_key1': 'val1', 'md_key2': 'val2'})
|
||||
image_ref_url = "%s/images/%s" % (glance.generate_glance_url(),
|
||||
uuids.fake_image_ref)
|
||||
image_ref_url = "%s/images/%s" % (
|
||||
glance.generate_glance_url(self.context), uuids.fake_image_ref)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
self.compute.terminate_instance(self.context, instance, [], [])
|
||||
|
||||
@ -485,8 +485,8 @@ class UsageInfoTestCase(test.TestCase):
|
||||
self.assertIn(attr, payload, "Key %s not in payload" % attr)
|
||||
self.assertEqual(payload['image_meta'],
|
||||
{'md_key1': 'val1', 'md_key2': 'val2'})
|
||||
image_ref_url = "%s/images/%s" % (glance.generate_glance_url(),
|
||||
uuids.fake_image_ref)
|
||||
image_ref_url = "%s/images/%s" % (
|
||||
glance.generate_glance_url(self.context), uuids.fake_image_ref)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
|
||||
def test_notify_about_instance_action(self):
|
||||
@ -778,8 +778,8 @@ class UsageInfoTestCase(test.TestCase):
|
||||
'audit_period_ending', 'image_meta'):
|
||||
self.assertIn(attr, payload, "Key %s not in payload" % attr)
|
||||
self.assertEqual(payload['image_meta'], {})
|
||||
image_ref_url = "%s/images/%s" % (glance.generate_glance_url(),
|
||||
uuids.fake_image_ref)
|
||||
image_ref_url = "%s/images/%s" % (
|
||||
glance.generate_glance_url(self.context), uuids.fake_image_ref)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
|
||||
def test_notify_about_instance_usage(self):
|
||||
@ -814,8 +814,8 @@ class UsageInfoTestCase(test.TestCase):
|
||||
self.assertEqual(payload['image_meta'],
|
||||
{'md_key1': 'val1', 'md_key2': 'val2'})
|
||||
self.assertEqual(payload['image_name'], 'fake_name')
|
||||
image_ref_url = "%s/images/%s" % (glance.generate_glance_url(),
|
||||
uuids.fake_image_ref)
|
||||
image_ref_url = "%s/images/%s" % (
|
||||
glance.generate_glance_url(self.context), uuids.fake_image_ref)
|
||||
self.assertEqual(payload['image_ref_url'], image_ref_url)
|
||||
self.compute.terminate_instance(self.context, instance, [], [])
|
||||
|
||||
|
@ -1580,7 +1580,7 @@ class TestGlanceApiServers(test.NoDBTestCase):
|
||||
'http://10.0.2.2:9294']
|
||||
expected_servers = set(glance_servers)
|
||||
self.flags(api_servers=glance_servers, group='glance')
|
||||
api_servers = glance.get_api_servers()
|
||||
api_servers = glance.get_api_servers('context')
|
||||
# In len(expected_servers) cycles, we should get all the endpoints
|
||||
self.assertEqual(expected_servers,
|
||||
{next(api_servers) for _ in expected_servers})
|
||||
@ -1589,7 +1589,7 @@ class TestGlanceApiServers(test.NoDBTestCase):
|
||||
def test_get_api_servers_get_ksa_adapter(self, mock_epd):
|
||||
"""Test get_api_servers via nova.utils.get_ksa_adapter()."""
|
||||
self.flags(api_servers=None, group='glance')
|
||||
api_servers = glance.get_api_servers()
|
||||
api_servers = glance.get_api_servers(mock.Mock())
|
||||
self.assertEqual(mock_epd.return_value.catalog_url, next(api_servers))
|
||||
# Still get itertools.cycle behavior
|
||||
self.assertEqual(mock_epd.return_value.catalog_url, next(api_servers))
|
||||
@ -1598,7 +1598,7 @@ class TestGlanceApiServers(test.NoDBTestCase):
|
||||
# Now test with endpoint_override - get_endpoint_data is not called.
|
||||
mock_epd.reset_mock()
|
||||
self.flags(endpoint_override='foo', group='glance')
|
||||
api_servers = glance.get_api_servers()
|
||||
api_servers = glance.get_api_servers(mock.Mock())
|
||||
self.assertEqual('foo', next(api_servers))
|
||||
self.assertEqual('foo', next(api_servers))
|
||||
mock_epd.assert_not_called()
|
||||
|
@ -97,7 +97,7 @@ class ContextTestCase(test.NoDBTestCase):
|
||||
service_catalog=None)
|
||||
self.assertEqual([], ctxt.service_catalog)
|
||||
|
||||
def test_service_catalog_cinder_only(self):
|
||||
def test_service_catalog_filter(self):
|
||||
service_catalog = [
|
||||
{u'type': u'compute', u'name': u'nova'},
|
||||
{u'type': u's3', u'name': u's3'},
|
||||
@ -110,7 +110,8 @@ class ContextTestCase(test.NoDBTestCase):
|
||||
{u'type': None, u'name': u'S_withouttype'},
|
||||
{u'type': u'vo', u'name': u'S_partofvolume'}]
|
||||
|
||||
volume_catalog = [{u'type': u'volumev3', u'name': u'cinderv3'},
|
||||
volume_catalog = [{u'type': u'image', u'name': u'glance'},
|
||||
{u'type': u'volumev3', u'name': u'cinderv3'},
|
||||
{u'type': u'block-storage', u'name': u'cinder'}]
|
||||
ctxt = context.RequestContext('111', '222',
|
||||
service_catalog=service_catalog)
|
||||
|
@ -20,6 +20,8 @@ import os.path
|
||||
import tempfile
|
||||
|
||||
import eventlet
|
||||
from keystoneauth1 import adapter as ks_adapter
|
||||
from keystoneauth1 import exceptions as ks_exc
|
||||
from keystoneauth1.identity import base as ks_identity
|
||||
from keystoneauth1 import session as ks_session
|
||||
import mock
|
||||
@ -1385,3 +1387,54 @@ class GetKSAAdapterTestCase(test.NoDBTestCase):
|
||||
self.load_adap.assert_called_once_with(
|
||||
utils.CONF, 'cinder', session=self.sess, auth=self.auth,
|
||||
min_version=None, max_version=None)
|
||||
|
||||
|
||||
class GetEndpointTestCase(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(GetEndpointTestCase, self).setUp()
|
||||
self.adap = mock.create_autospec(ks_adapter.Adapter, instance=True)
|
||||
self.adap.endpoint_override = None
|
||||
self.adap.service_type = 'stype'
|
||||
self.adap.interface = ['admin', 'public']
|
||||
|
||||
def test_endpoint_override(self):
|
||||
self.adap.endpoint_override = 'foo'
|
||||
self.assertEqual('foo', utils.get_endpoint(self.adap))
|
||||
self.adap.get_endpoint_data.assert_not_called()
|
||||
self.adap.get_endpoint.assert_not_called()
|
||||
|
||||
def test_image_good(self):
|
||||
self.adap.service_type = 'image'
|
||||
self.adap.get_endpoint_data.return_value.catalog_url = 'url'
|
||||
self.assertEqual('url', utils.get_endpoint(self.adap))
|
||||
self.adap.get_endpoint_data.assert_called_once_with()
|
||||
self.adap.get_endpoint.assert_not_called()
|
||||
|
||||
def test_image_bad(self):
|
||||
self.adap.service_type = 'image'
|
||||
self.adap.get_endpoint_data.side_effect = AttributeError
|
||||
self.adap.get_endpoint.return_value = 'url'
|
||||
self.assertEqual('url', utils.get_endpoint(self.adap))
|
||||
self.adap.get_endpoint_data.assert_called_once_with()
|
||||
self.adap.get_endpoint.assert_called_once_with()
|
||||
|
||||
def test_nonimage_good(self):
|
||||
self.adap.get_endpoint.return_value = 'url'
|
||||
self.assertEqual('url', utils.get_endpoint(self.adap))
|
||||
self.adap.get_endpoint_data.assert_not_called()
|
||||
self.adap.get_endpoint.assert_called_once_with()
|
||||
|
||||
def test_nonimage_try_interfaces(self):
|
||||
self.adap.get_endpoint.side_effect = (ks_exc.EndpointNotFound, 'url')
|
||||
self.assertEqual('url', utils.get_endpoint(self.adap))
|
||||
self.adap.get_endpoint_data.assert_not_called()
|
||||
self.assertEqual(2, self.adap.get_endpoint.call_count)
|
||||
self.assertEqual('admin', self.adap.interface)
|
||||
|
||||
def test_nonimage_try_interfaces_fail(self):
|
||||
self.adap.get_endpoint.side_effect = ks_exc.EndpointNotFound
|
||||
self.assertRaises(ks_exc.EndpointNotFound,
|
||||
utils.get_endpoint, self.adap)
|
||||
self.adap.get_endpoint_data.assert_not_called()
|
||||
self.assertEqual(3, self.adap.get_endpoint.call_count)
|
||||
self.assertEqual('public', self.adap.interface)
|
||||
|
@ -33,6 +33,7 @@ import tempfile
|
||||
import time
|
||||
|
||||
import eventlet
|
||||
from keystoneauth1 import exceptions as ks_exc
|
||||
from keystoneauth1 import loading as ks_loading
|
||||
import netaddr
|
||||
from os_service_types import service_types
|
||||
@ -1325,3 +1326,56 @@ def get_ksa_adapter(service_type, ksa_auth=None, ksa_session=None,
|
||||
return ks_loading.load_adapter_from_conf_options(
|
||||
CONF, confgrp, session=ksa_session, auth=ksa_auth,
|
||||
min_version=min_version, max_version=max_version)
|
||||
|
||||
|
||||
def get_endpoint(ksa_adapter):
|
||||
"""Get the endpoint URL represented by a keystoneauth1 Adapter.
|
||||
|
||||
This method is equivalent to what
|
||||
|
||||
ksa_adapter.get_endpoint()
|
||||
|
||||
should do, if it weren't for a panoply of bugs.
|
||||
|
||||
:param ksa_adapter: keystoneauth1.adapter.Adapter, appropriately set up
|
||||
with an endpoint_override; or service_type, interface
|
||||
(list) and auth/service_catalog.
|
||||
:return: String endpoint URL.
|
||||
:raise EndpointNotFound: If endpoint discovery fails.
|
||||
"""
|
||||
# TODO(efried): This will be unnecessary once bug #1707993 is fixed.
|
||||
# (At least for the non-image case, until 1707995 is fixed.)
|
||||
if ksa_adapter.endpoint_override:
|
||||
return ksa_adapter.endpoint_override
|
||||
# TODO(efried): Remove this once bug #1707995 is fixed.
|
||||
if ksa_adapter.service_type == 'image':
|
||||
try:
|
||||
return ksa_adapter.get_endpoint_data().catalog_url
|
||||
except AttributeError:
|
||||
# ksa_adapter.auth is a _ContextAuthPlugin, which doesn't have
|
||||
# get_endpoint_data. Fall through to using get_endpoint().
|
||||
pass
|
||||
# TODO(efried): The remainder of this method reduces to
|
||||
# TODO(efried): return ksa_adapter.get_endpoint()
|
||||
# TODO(efried): once bug #1709118 is fixed.
|
||||
# NOTE(efried): Id9bd19cca68206fc64d23b0eaa95aa3e5b01b676 may also do the
|
||||
# trick, once it's in a ksa release.
|
||||
# The EndpointNotFound exception happens when _ContextAuthPlugin is in play
|
||||
# because its get_endpoint() method isn't yet set up to handle interface as
|
||||
# a list. (It could also happen with a real auth if the endpoint isn't
|
||||
# there; but that's covered below.)
|
||||
try:
|
||||
return ksa_adapter.get_endpoint()
|
||||
except ks_exc.EndpointNotFound:
|
||||
pass
|
||||
|
||||
interfaces = list(ksa_adapter.interface)
|
||||
for interface in interfaces:
|
||||
ksa_adapter.interface = interface
|
||||
try:
|
||||
return ksa_adapter.get_endpoint()
|
||||
except ks_exc.EndpointNotFound:
|
||||
pass
|
||||
raise ks_exc.EndpointNotFound(
|
||||
"Could not find requested endpoint for any of the following "
|
||||
"interfaces: %s" % interfaces)
|
||||
|
@ -35,7 +35,7 @@ LOG = logging.getLogger(__name__)
|
||||
class GlanceStore(object):
|
||||
def _call_glance_plugin(self, context, instance, session, fn, image_id,
|
||||
params):
|
||||
glance_api_servers = glance.get_api_servers()
|
||||
glance_api_servers = glance.get_api_servers(context)
|
||||
sr_path = vm_utils.get_sr_path(session)
|
||||
extra_headers = glance.generate_identity_headers(context)
|
||||
|
||||
|
@ -2,9 +2,10 @@
|
||||
upgrade:
|
||||
- |
|
||||
Nova now uses keystoneauth1 configuration to set up communication with the
|
||||
image service. Use keystoneauth1 loading parameters for auth, Session, and
|
||||
image service. Use keystoneauth1 loading parameters for Session and
|
||||
Adapter setup in the ``[glance]`` conf section. This includes using
|
||||
``endpoint_override`` in favor of ``api_servers``. The
|
||||
``[glance]api_servers`` conf option is still supported, but should only be
|
||||
used if you need multiple endpoints and are unable to use a load balancer
|
||||
for some reason.
|
||||
for some reason. However, note that no configuration is necessary with an
|
||||
appropriate service catalog entry for the image service.
|
||||
|
Loading…
x
Reference in New Issue
Block a user