summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Untz <vuntz@suse.com>2012-08-22 15:40:59 +0200
committerVincent Untz <vuntz@suse.com>2012-08-27 08:48:18 +0200
commitfa5be443bae880ab15d5079caa28d6862cbd13b9 (patch)
treec40e4f638deaec2b2215ec95560579593e4c3180
parent68e9a9e351e5b7bc91148a939d470ba04a525020 (diff)
downloadnova-fa5be443bae880ab15d5079caa28d6862cbd13b9.tar.gz
nova-fa5be443bae880ab15d5079caa28d6862cbd13b9.tar.xz
nova-fa5be443bae880ab15d5079caa28d6862cbd13b9.zip
Allow connecting to a ssl-based glance
This introduces a new glance_api_insecure setting that can be used to not verify the certificate of the glance server against the certificate authorities. Fix bug 1042081. Change-Id: I0a9f081425854e9c01e00dfd641e42276c878c67
-rw-r--r--etc/nova/nova.conf.sample6
-rw-r--r--nova/flags.py7
-rw-r--r--nova/image/glance.py42
-rw-r--r--nova/tests/image/test_glance.py39
-rw-r--r--nova/virt/xenapi/vm_utils.py6
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/glance30
6 files changed, 93 insertions, 37 deletions
diff --git a/etc/nova/nova.conf.sample b/etc/nova/nova.conf.sample
index d31d1e9e6..93e24877e 100644
--- a/etc/nova/nova.conf.sample
+++ b/etc/nova/nova.conf.sample
@@ -125,9 +125,13 @@
#### (IntOpt) default glance port
# glance_api_servers=$glance_host:$glance_port
-#### (ListOpt) A list of the glance api servers available to nova
+#### (ListOpt) A list of the glance api servers available to nova.
+#### Prefix with https:// for ssl-based glance api servers.
#### ([hostname|ip]:port)
+# glance_api_insecure=false
+#### (BoolOpt) If passed, allow to perform insecure SSL (https) requests to glance
+
# glance_num_retries=0
#### (IntOpt) Number retries when downloading an image from glance
diff --git a/nova/flags.py b/nova/flags.py
index 08a136963..9ca1577df 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -137,8 +137,13 @@ global_opts = [
help='default glance port'),
cfg.ListOpt('glance_api_servers',
default=['$glance_host:$glance_port'],
- help='A list of the glance api servers available to nova '
+ help='A list of the glance api servers available to nova. '
+ 'Prefix with https:// for ssl-based glance api servers. '
'([hostname|ip]:port)'),
+ cfg.BoolOpt('glance_api_insecure',
+ default=False,
+ help='Allow to perform insecure SSL (https) requests to '
+ 'glance'),
cfg.IntOpt('glance_num_retries',
default=0,
help='Number retries when downloading an image from glance'),
diff --git a/nova/image/glance.py b/nova/image/glance.py
index 7f2dd74f1..38508a12c 100644
--- a/nova/image/glance.py
+++ b/nova/image/glance.py
@@ -52,15 +52,21 @@ def _parse_image_ref(image_href):
port = o.port or 80
host = o.netloc.split(':', 1)[0]
image_id = o.path.split('/')[-1]
- return (image_id, host, port)
+ use_ssl = (o.scheme == 'https')
+ return (image_id, host, port, use_ssl)
-def _create_glance_client(context, host, port):
+def _create_glance_client(context, host, port, use_ssl):
"""Instantiate a new glanceclient.Client object"""
+ if use_ssl:
+ scheme = 'https'
+ else:
+ scheme = 'http'
params = {}
+ params['insecure'] = FLAGS.glance_api_insecure
if FLAGS.auth_strategy == 'keystone':
params['token'] = context.auth_token
- endpoint = 'http://%s:%s' % (host, port)
+ endpoint = '%s://%s:%s' % (scheme, host, port)
return glanceclient.Client('1', endpoint, **params)
@@ -72,8 +78,13 @@ def get_api_servers():
"""
api_servers = []
for api_server in FLAGS.glance_api_servers:
- host, port_str = api_server.split(':')
- api_servers.append((host, int(port_str)))
+ if '//' not in api_server:
+ api_server = 'http://' + api_server
+ o = urlparse.urlparse(api_server)
+ port = o.port or 80
+ host = o.netloc.split(':', 1)[0]
+ use_ssl = (o.scheme == 'https')
+ api_servers.append((host, port, use_ssl))
random.shuffle(api_servers)
return itertools.cycle(api_servers)
@@ -81,25 +92,29 @@ def get_api_servers():
class GlanceClientWrapper(object):
"""Glance client wrapper class that implements retries."""
- def __init__(self, context=None, host=None, port=None):
+ def __init__(self, context=None, host=None, port=None, use_ssl=False):
if host is not None:
- self.client = self._create_static_client(context, host, port)
+ self.client = self._create_static_client(context,
+ host, port, use_ssl)
else:
self.client = None
self.api_servers = None
- def _create_static_client(self, context, host, port):
+ def _create_static_client(self, context, host, port, use_ssl):
"""Create a client that we'll use for every call."""
self.host = host
self.port = port
- return _create_glance_client(context, self.host, self.port)
+ self.use_ssl = use_ssl
+ return _create_glance_client(context,
+ self.host, self.port, self.use_ssl)
def _create_onetime_client(self, context):
"""Create a client that will be used for one call."""
if self.api_servers is None:
self.api_servers = get_api_servers()
- self.host, self.port = self.api_servers.next()
- return _create_glance_client(context, self.host, self.port)
+ self.host, self.port, self.use_ssl = self.api_servers.next()
+ return _create_glance_client(context,
+ self.host, self.port, self.use_ssl)
def call(self, context, method, *args, **kwargs):
"""
@@ -398,9 +413,10 @@ def get_remote_image_service(context, image_href):
return image_service, image_href
try:
- (image_id, glance_host, glance_port) = _parse_image_ref(image_href)
+ (image_id, glance_host, glance_port, use_ssl) = \
+ _parse_image_ref(image_href)
glance_client = GlanceClientWrapper(context=context,
- host=glance_host, port=glance_port)
+ host=glance_host, port=glance_port, use_ssl=use_ssl)
except ValueError:
raise exception.InvalidImageRef(image_href=image_href)
diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py
index 5b695d2f7..35b3648bb 100644
--- a/nova/tests/image/test_glance.py
+++ b/nova/tests/image/test_glance.py
@@ -105,7 +105,7 @@ class TestGlanceImageService(test.TestCase):
self.context = context.RequestContext('fake', 'fake', auth_token=True)
def _create_image_service(self, client):
- def _fake_create_glance_client(context, host, port):
+ def _fake_create_glance_client(context, host, port, use_ssl):
return client
self.stubs.Set(glance, '_create_glance_client',
@@ -550,8 +550,9 @@ class TestGlanceClientWrapper(test.TestCase):
def setUp(self):
super(TestGlanceClientWrapper, self).setUp()
- self.flags(glance_api_servers=['host1:9292', 'host2:9293',
- 'host3:9294'])
+ # host1 has no scheme, which is http by default
+ self.flags(glance_api_servers=['host1:9292', 'https://host2:9293',
+ 'http://host3:9294'])
# Make the test run fast
def _fake_sleep(secs):
@@ -564,19 +565,21 @@ class TestGlanceClientWrapper(test.TestCase):
ctxt = context.RequestContext('fake', 'fake')
fake_host = 'host4'
fake_port = 9295
+ fake_use_ssl = False
info = {'num_calls': 0}
- def _fake_create_glance_client(context, host, port):
+ def _fake_create_glance_client(context, host, port, use_ssl):
self.assertEqual(host, fake_host)
self.assertEqual(port, fake_port)
+ self.assertEqual(use_ssl, fake_use_ssl)
return _create_failing_glance_client(info)
self.stubs.Set(glance, '_create_glance_client',
_fake_create_glance_client)
client = glance.GlanceClientWrapper(context=ctxt,
- host=fake_host, port=fake_port)
+ host=fake_host, port=fake_port, use_ssl=fake_use_ssl)
self.assertRaises(exception.GlanceConnectionFailed,
client.call, ctxt, 'get', 'meow')
self.assertEqual(info['num_calls'], 1)
@@ -588,15 +591,17 @@ class TestGlanceClientWrapper(test.TestCase):
info = {'num_calls': 0,
'host': 'host1',
- 'port': 9292}
+ 'port': 9292,
+ 'use_ssl': False}
# Leave the list in a known-order
def _fake_shuffle(servers):
pass
- def _fake_create_glance_client(context, host, port):
+ def _fake_create_glance_client(context, host, port, use_ssl):
self.assertEqual(host, info['host'])
self.assertEqual(port, info['port'])
+ self.assertEqual(use_ssl, info['use_ssl'])
return _create_failing_glance_client(info)
self.stubs.Set(random, 'shuffle', _fake_shuffle)
@@ -611,7 +616,8 @@ class TestGlanceClientWrapper(test.TestCase):
info = {'num_calls': 0,
'host': 'host2',
- 'port': 9293}
+ 'port': 9293,
+ 'use_ssl': True}
def _fake_shuffle2(servers):
# fake shuffle in a known manner
@@ -629,19 +635,21 @@ class TestGlanceClientWrapper(test.TestCase):
ctxt = context.RequestContext('fake', 'fake')
fake_host = 'host4'
fake_port = 9295
+ fake_use_ssl = False
info = {'num_calls': 0}
- def _fake_create_glance_client(context, host, port):
+ def _fake_create_glance_client(context, host, port, use_ssl):
self.assertEqual(host, fake_host)
self.assertEqual(port, fake_port)
+ self.assertEqual(use_ssl, fake_use_ssl)
return _create_failing_glance_client(info)
self.stubs.Set(glance, '_create_glance_client',
_fake_create_glance_client)
client = glance.GlanceClientWrapper(context=ctxt,
- host=fake_host, port=fake_port)
+ host=fake_host, port=fake_port, use_ssl=fake_use_ssl)
client.call(ctxt, 'get', 'meow')
self.assertEqual(info['num_calls'], 2)
@@ -653,17 +661,20 @@ class TestGlanceClientWrapper(test.TestCase):
info = {'num_calls': 0,
'host0': 'host1',
'port0': 9292,
+ 'use_ssl0': False,
'host1': 'host2',
- 'port1': 9293}
+ 'port1': 9293,
+ 'use_ssl1': True}
# Leave the list in a known-order
def _fake_shuffle(servers):
pass
- def _fake_create_glance_client(context, host, port):
+ def _fake_create_glance_client(context, host, port, use_ssl):
attempt = info['num_calls']
self.assertEqual(host, info['host%s' % attempt])
self.assertEqual(port, info['port%s' % attempt])
+ self.assertEqual(use_ssl, info['use_ssl%s' % attempt])
return _create_failing_glance_client(info)
self.stubs.Set(random, 'shuffle', _fake_shuffle)
@@ -684,8 +695,10 @@ class TestGlanceClientWrapper(test.TestCase):
info = {'num_calls': 0,
'host0': 'host2',
'port0': 9293,
+ 'use_ssl0': True,
'host1': 'host3',
- 'port1': 9294}
+ 'port1': 9294,
+ 'use_ssl1': False}
client2.call(ctxt, 'get', 'meow')
self.assertEqual(info['num_calls'], 2)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 744770528..97650b248 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -649,7 +649,7 @@ def upload_image(context, session, instance, vdi_uuids, image_id):
" ID %(image_id)s"), locals(), instance=instance)
glance_api_servers = glance.get_api_servers()
- glance_host, glance_port = glance_api_servers.next()
+ glance_host, glance_port, glance_use_ssl = glance_api_servers.next()
# TODO(sirp): this inherit-image-property code should probably go in
# nova/compute/manager so it can be shared across hypervisors
@@ -669,6 +669,7 @@ def upload_image(context, session, instance, vdi_uuids, image_id):
'image_id': image_id,
'glance_host': glance_host,
'glance_port': glance_port,
+ 'glance_use_ssl': glance_use_ssl,
'sr_path': get_sr_path(session),
'auth_token': getattr(context, 'auth_token', None),
'properties': properties}
@@ -1011,9 +1012,10 @@ def _fetch_vhd_image(context, session, instance, image_id):
glance_api_servers = glance.get_api_servers()
def pick_glance(params):
- glance_host, glance_port = glance_api_servers.next()
+ glance_host, glance_port, glance_use_ssl = glance_api_servers.next()
params['glance_host'] = glance_host
params['glance_port'] = glance_port
+ params['glance_use_ssl'] = glance_use_ssl
plugin_name = 'glance'
vdis = _fetch_using_dom0_plugin_with_retry(
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index 2f0050f11..a574bb406 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -95,7 +95,7 @@ def _download_tarball_and_verify(request, staging_path):
def _download_tarball(sr_path, staging_path, image_id, glance_host,
- glance_port, auth_token):
+ glance_port, glance_use_ssl, auth_token):
"""Download the tarball image from Glance and extract it into the staging
area. Retry if there is any failure.
"""
@@ -104,7 +104,12 @@ def _download_tarball(sr_path, staging_path, image_id, glance_host,
if auth_token:
headers['x-auth-token'] = auth_token
- url = ("http://%(glance_host)s:%(glance_port)d/v1/images/"
+ if glance_use_ssl:
+ scheme = 'https'
+ else:
+ scheme = 'http'
+
+ url = ("%(scheme)s://%(glance_host)s:%(glance_port)d/v1/images/"
"%(image_id)s" % locals())
logging.info("Downloading %s" % url)
@@ -117,14 +122,23 @@ def _download_tarball(sr_path, staging_path, image_id, glance_host,
def _upload_tarball(staging_path, image_id, glance_host, glance_port,
- auth_token, properties):
+ glance_use_ssl, auth_token, properties):
"""
Create a tarball of the image and then stream that into Glance
using chunked-transfer-encoded HTTP.
"""
- url = 'http://%s:%s/v1/images/%s' % (glance_host, glance_port, image_id)
+ if glance_use_ssl:
+ scheme = 'https'
+ else:
+ scheme = 'http'
+
+ url = '%s://%s:%s/v1/images/%s' % (scheme, glance_host, glance_port,
+ image_id)
logging.info("Writing image data to %s" % url)
- conn = httplib.HTTPConnection(glance_host, glance_port)
+ if glance_use_ssl:
+ conn = httplib.HTTPSConnection(glance_host, glance_port)
+ else:
+ conn = httplib.HTTPConnection(glance_host, glance_port)
# NOTE(sirp): httplib under python2.4 won't accept a file-like object
# to request
@@ -196,6 +210,7 @@ def download_vhd(session, args):
image_id = params["image_id"]
glance_host = params["glance_host"]
glance_port = params["glance_port"]
+ glance_use_ssl = params["glance_use_ssl"]
uuid_stack = params["uuid_stack"]
sr_path = params["sr_path"]
auth_token = params["auth_token"]
@@ -205,7 +220,7 @@ def download_vhd(session, args):
# Download tarball into staging area and extract it
_download_tarball(
sr_path, staging_path, image_id, glance_host, glance_port,
- auth_token)
+ glance_use_ssl, auth_token)
# Move the VHDs from the staging area into the storage repository
imported_vhds = utils.import_vhds(sr_path, staging_path, uuid_stack)
@@ -225,6 +240,7 @@ def upload_vhd(session, args):
image_id = params["image_id"]
glance_host = params["glance_host"]
glance_port = params["glance_port"]
+ glance_use_ssl = params["glance_use_ssl"]
sr_path = params["sr_path"]
auth_token = params["auth_token"]
properties = params["properties"]
@@ -233,7 +249,7 @@ def upload_vhd(session, args):
try:
utils.prepare_staging_area(sr_path, staging_path, vdi_uuids)
_upload_tarball(staging_path, image_id, glance_host, glance_port,
- auth_token, properties)
+ glance_use_ssl, auth_token, properties)
finally:
utils.cleanup_staging_area(staging_path)