Merge "Enable radosgw support in ironic"
This commit is contained in:
commit
5ca6e64e33
@ -906,32 +906,34 @@
|
|||||||
|
|
||||||
# The "endpoint" (scheme, hostname, optional port) for the
|
# The "endpoint" (scheme, hostname, optional port) for the
|
||||||
# Swift URL of the form
|
# Swift URL of the form
|
||||||
# "endpoint_url/api_version/account/container/object_id". Do
|
# "endpoint_url/api_version/[account/]container/object_id". Do
|
||||||
# not include trailing "/". For example, use
|
# not include trailing "/". For example, use
|
||||||
# "https://swift.example.com". Required for temporary URLs.
|
# "https://swift.example.com". In case of using RADOS Gateway,
|
||||||
# (string value)
|
# endpoint may also contain /swift path, if it does not, it
|
||||||
|
# will be appended. Required for temporary URLs. (string
|
||||||
|
# value)
|
||||||
#swift_endpoint_url=<None>
|
#swift_endpoint_url=<None>
|
||||||
|
|
||||||
# The Swift API version to create a temporary URL for.
|
# The Swift API version to create a temporary URL for.
|
||||||
# Defaults to "v1". Swift temporary URL format:
|
# Defaults to "v1". Swift temporary URL format:
|
||||||
# "endpoint_url/api_version/account/container/object_id"
|
# "endpoint_url/api_version/[account/]container/object_id"
|
||||||
# (string value)
|
# (string value)
|
||||||
#swift_api_version=v1
|
#swift_api_version=v1
|
||||||
|
|
||||||
# The account that Glance uses to communicate with Swift. The
|
# The account that Glance uses to communicate with Swift. The
|
||||||
# format is "AUTH_uuid". "uuid" is the UUID for the account
|
# format is "AUTH_uuid". "uuid" is the UUID for the account
|
||||||
# configured in the glance-api.conf. Required for temporary
|
# configured in the glance-api.conf. Required for temporary
|
||||||
# URLs. For example:
|
# URLs when Glance backend is Swift. For example:
|
||||||
# "AUTH_a422b2-91f3-2f46-74b7-d7c9e8958f5d30". Swift temporary
|
# "AUTH_a422b2-91f3-2f46-74b7-d7c9e8958f5d30". Swift temporary
|
||||||
# URL format:
|
# URL format:
|
||||||
# "endpoint_url/api_version/account/container/object_id"
|
# "endpoint_url/api_version/[account/]container/object_id"
|
||||||
# (string value)
|
# (string value)
|
||||||
#swift_account=<None>
|
#swift_account=<None>
|
||||||
|
|
||||||
# The Swift container Glance is configured to store its images
|
# The Swift container Glance is configured to store its images
|
||||||
# in. Defaults to "glance", which is the default in glance-
|
# in. Defaults to "glance", which is the default in glance-
|
||||||
# api.conf. Swift temporary URL format:
|
# api.conf. Swift temporary URL format:
|
||||||
# "endpoint_url/api_version/account/container/object_id"
|
# "endpoint_url/api_version/[account/]container/object_id"
|
||||||
# (string value)
|
# (string value)
|
||||||
#swift_container=glance
|
#swift_container=glance
|
||||||
|
|
||||||
@ -944,6 +946,11 @@
|
|||||||
# value)
|
# value)
|
||||||
#swift_store_multiple_containers_seed=0
|
#swift_store_multiple_containers_seed=0
|
||||||
|
|
||||||
|
# Type of the endpoint to use for temporary URLs. It depends
|
||||||
|
# on an actual Glance backend used. Possible values are
|
||||||
|
# "swift" and "radosgw". (string value)
|
||||||
|
#temp_url_endpoint_type=swift
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Options defined in ironic.common.image_service
|
# Options defined in ironic.common.image_service
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
from six.moves.urllib import parse as urlparse
|
||||||
from swiftclient import utils as swift_utils
|
from swiftclient import utils as swift_utils
|
||||||
|
|
||||||
from ironic.common import exception as exc
|
from ironic.common import exception as exc
|
||||||
@ -31,7 +32,10 @@ glance_opts = [
|
|||||||
'via the direct_url. Currently supported schemes: '
|
'via the direct_url. Currently supported schemes: '
|
||||||
'[file].')),
|
'[file].')),
|
||||||
# To upload this key to Swift:
|
# To upload this key to Swift:
|
||||||
# swift post -m Temp-Url-Key:correcthorsebatterystaple
|
# swift post -m Temp-Url-Key:secretkey
|
||||||
|
# When using radosgw, temp url key could be uploaded via the above swift
|
||||||
|
# command, or with:
|
||||||
|
# radosgw-admin user modify --uid=user --temp-url-key=secretkey
|
||||||
cfg.StrOpt('swift_temp_url_key',
|
cfg.StrOpt('swift_temp_url_key',
|
||||||
help=_('The secret token given to Swift to allow temporary URL '
|
help=_('The secret token given to Swift to allow temporary URL '
|
||||||
'downloads. Required for temporary URLs.'),
|
'downloads. Required for temporary URLs.'),
|
||||||
@ -47,25 +51,27 @@ glance_opts = [
|
|||||||
'swift_endpoint_url',
|
'swift_endpoint_url',
|
||||||
help=_('The "endpoint" (scheme, hostname, optional port) for '
|
help=_('The "endpoint" (scheme, hostname, optional port) for '
|
||||||
'the Swift URL of the form '
|
'the Swift URL of the form '
|
||||||
'"endpoint_url/api_version/account/container/object_id". '
|
'"endpoint_url/api_version/[account/]container/object_id". '
|
||||||
'Do not include trailing "/". '
|
'Do not include trailing "/". '
|
||||||
'For example, use "https://swift.example.com". '
|
'For example, use "https://swift.example.com". In case of '
|
||||||
|
'using RADOS Gateway, endpoint may also contain /swift path, '
|
||||||
|
'if it does not, it will be appended. '
|
||||||
'Required for temporary URLs.')),
|
'Required for temporary URLs.')),
|
||||||
cfg.StrOpt(
|
cfg.StrOpt(
|
||||||
'swift_api_version',
|
'swift_api_version',
|
||||||
default='v1',
|
default='v1',
|
||||||
help=_('The Swift API version to create a temporary URL for. '
|
help=_('The Swift API version to create a temporary URL for. '
|
||||||
'Defaults to "v1". Swift temporary URL format: '
|
'Defaults to "v1". Swift temporary URL format: '
|
||||||
'"endpoint_url/api_version/account/container/object_id"')),
|
'"endpoint_url/api_version/[account/]container/object_id"')),
|
||||||
cfg.StrOpt(
|
cfg.StrOpt(
|
||||||
'swift_account',
|
'swift_account',
|
||||||
help=_('The account that Glance uses to communicate with '
|
help=_('The account that Glance uses to communicate with '
|
||||||
'Swift. The format is "AUTH_uuid". "uuid" is the '
|
'Swift. The format is "AUTH_uuid". "uuid" is the '
|
||||||
'UUID for the account configured in the glance-api.conf. '
|
'UUID for the account configured in the glance-api.conf. '
|
||||||
'Required for temporary URLs. For example: '
|
'Required for temporary URLs when Glance backend is Swift. '
|
||||||
'"AUTH_a422b2-91f3-2f46-74b7-d7c9e8958f5d30". '
|
'For example: "AUTH_a422b2-91f3-2f46-74b7-d7c9e8958f5d30". '
|
||||||
'Swift temporary URL format: '
|
'Swift temporary URL format: '
|
||||||
'"endpoint_url/api_version/account/container/object_id"')),
|
'"endpoint_url/api_version/[account/]container/object_id"')),
|
||||||
cfg.StrOpt(
|
cfg.StrOpt(
|
||||||
'swift_container',
|
'swift_container',
|
||||||
default='glance',
|
default='glance',
|
||||||
@ -73,7 +79,7 @@ glance_opts = [
|
|||||||
'images in. Defaults to "glance", which is the default '
|
'images in. Defaults to "glance", which is the default '
|
||||||
'in glance-api.conf. '
|
'in glance-api.conf. '
|
||||||
'Swift temporary URL format: '
|
'Swift temporary URL format: '
|
||||||
'"endpoint_url/api_version/account/container/object_id"')),
|
'"endpoint_url/api_version/[account/]container/object_id"')),
|
||||||
cfg.IntOpt('swift_store_multiple_containers_seed',
|
cfg.IntOpt('swift_store_multiple_containers_seed',
|
||||||
default=0,
|
default=0,
|
||||||
help=_('This should match a config by the same name in the '
|
help=_('This should match a config by the same name in the '
|
||||||
@ -83,6 +89,11 @@ glance_opts = [
|
|||||||
'value between 1 and 32, a single-tenant store will use '
|
'value between 1 and 32, a single-tenant store will use '
|
||||||
'multiple containers to store images, and this value '
|
'multiple containers to store images, and this value '
|
||||||
'will determine how many containers are created.')),
|
'will determine how many containers are created.')),
|
||||||
|
cfg.StrOpt('temp_url_endpoint_type',
|
||||||
|
default='swift',
|
||||||
|
help=_('Type of the endpoint to use for temporary URLs. It '
|
||||||
|
'depends on an actual Glance backend used. Possible '
|
||||||
|
'values are "swift" and "radosgw".'))
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -146,14 +157,28 @@ class GlanceImageService(base_image_service.BaseImageService,
|
|||||||
% image_info)
|
% image_info)
|
||||||
|
|
||||||
url_fragments = {
|
url_fragments = {
|
||||||
'endpoint_url': CONF.glance.swift_endpoint_url,
|
|
||||||
'api_version': CONF.glance.swift_api_version,
|
'api_version': CONF.glance.swift_api_version,
|
||||||
'account': CONF.glance.swift_account,
|
'account': CONF.glance.swift_account,
|
||||||
'container': self._get_swift_container(image_info['id']),
|
'container': self._get_swift_container(image_info['id']),
|
||||||
'object_id': image_info['id']
|
'object_id': image_info['id']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endpoint_url = CONF.glance.swift_endpoint_url
|
||||||
|
if CONF.glance.temp_url_endpoint_type == 'radosgw':
|
||||||
|
chunks = urlparse.urlsplit(CONF.glance.swift_endpoint_url)
|
||||||
|
if not chunks.path:
|
||||||
|
endpoint_url = urlparse.urljoin(
|
||||||
|
endpoint_url, 'swift')
|
||||||
|
elif chunks.path != '/swift':
|
||||||
|
raise exc.InvalidParameterValue(
|
||||||
|
_('Swift endpoint URL should only contain scheme, '
|
||||||
|
'hostname, optional port and optional /swift path '
|
||||||
|
'without trailing slash; provided value is: %s')
|
||||||
|
% endpoint_url)
|
||||||
|
template = '/{api_version}/{container}/{object_id}'
|
||||||
|
else:
|
||||||
template = '/{api_version}/{account}/{container}/{object_id}'
|
template = '/{api_version}/{account}/{container}/{object_id}'
|
||||||
|
|
||||||
url_path = template.format(**url_fragments)
|
url_path = template.format(**url_fragments)
|
||||||
path = swift_utils.generate_temp_url(
|
path = swift_utils.generate_temp_url(
|
||||||
path=url_path,
|
path=url_path,
|
||||||
@ -162,7 +187,7 @@ class GlanceImageService(base_image_service.BaseImageService,
|
|||||||
method='GET')
|
method='GET')
|
||||||
|
|
||||||
return '{endpoint_url}{url_path}'.format(
|
return '{endpoint_url}{url_path}'.format(
|
||||||
endpoint_url=url_fragments['endpoint_url'], url_path=path)
|
endpoint_url=endpoint_url, url_path=path)
|
||||||
|
|
||||||
def _validate_temp_url_config(self):
|
def _validate_temp_url_config(self):
|
||||||
"""Validate the required settings for a temporary URL."""
|
"""Validate the required settings for a temporary URL."""
|
||||||
@ -174,7 +199,8 @@ class GlanceImageService(base_image_service.BaseImageService,
|
|||||||
raise exc.MissingParameterValue(_(
|
raise exc.MissingParameterValue(_(
|
||||||
'Swift temporary URLs require a Swift endpoint URL. '
|
'Swift temporary URLs require a Swift endpoint URL. '
|
||||||
'You must provide "swift_endpoint_url" as a config option.'))
|
'You must provide "swift_endpoint_url" as a config option.'))
|
||||||
if not CONF.glance.swift_account:
|
if (not CONF.glance.swift_account and
|
||||||
|
CONF.glance.temp_url_endpoint_type == 'swift'):
|
||||||
raise exc.MissingParameterValue(_(
|
raise exc.MissingParameterValue(_(
|
||||||
'Swift temporary URLs require a Swift account string. '
|
'Swift temporary URLs require a Swift account string. '
|
||||||
'You must provide "swift_account" as a config option.'))
|
'You must provide "swift_account" as a config option.'))
|
||||||
|
@ -25,6 +25,7 @@ import mock
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_context import context
|
from oslo_context import context
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
|
from six.moves.urllib import parse as urlparse
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
@ -684,6 +685,65 @@ class TestGlanceSwiftTempURL(base.TestCase):
|
|||||||
key=CONF.glance.swift_temp_url_key,
|
key=CONF.glance.swift_temp_url_key,
|
||||||
method='GET')
|
method='GET')
|
||||||
|
|
||||||
|
@mock.patch('swiftclient.utils.generate_temp_url', autospec=True)
|
||||||
|
def test_swift_temp_url_radosgw(self, tempurl_mock):
|
||||||
|
self.config(temp_url_endpoint_type='radosgw', group='glance')
|
||||||
|
path = ('/v1'
|
||||||
|
'/glance'
|
||||||
|
'/757274c4-2856-4bd2-bb20-9a4a231e187b')
|
||||||
|
tempurl_mock.return_value = (
|
||||||
|
path + '?temp_url_sig=hmacsig&temp_url_expires=1400001200')
|
||||||
|
|
||||||
|
self.service._validate_temp_url_config = mock.Mock()
|
||||||
|
|
||||||
|
temp_url = self.service.swift_temp_url(image_info=self.fake_image)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
(urlparse.urljoin(CONF.glance.swift_endpoint_url, 'swift') +
|
||||||
|
tempurl_mock.return_value),
|
||||||
|
temp_url)
|
||||||
|
tempurl_mock.assert_called_with(
|
||||||
|
path=path,
|
||||||
|
seconds=CONF.glance.swift_temp_url_duration,
|
||||||
|
key=CONF.glance.swift_temp_url_key,
|
||||||
|
method='GET')
|
||||||
|
|
||||||
|
@mock.patch('swiftclient.utils.generate_temp_url', autospec=True)
|
||||||
|
def test_swift_temp_url_radosgw_endpoint_with_swift(self, tempurl_mock):
|
||||||
|
self.config(swift_endpoint_url='https://swift.radosgw.com/swift',
|
||||||
|
group='glance')
|
||||||
|
self.config(temp_url_endpoint_type='radosgw', group='glance')
|
||||||
|
path = ('/v1'
|
||||||
|
'/glance'
|
||||||
|
'/757274c4-2856-4bd2-bb20-9a4a231e187b')
|
||||||
|
tempurl_mock.return_value = (
|
||||||
|
path + '?temp_url_sig=hmacsig&temp_url_expires=1400001200')
|
||||||
|
|
||||||
|
self.service._validate_temp_url_config = mock.Mock()
|
||||||
|
|
||||||
|
temp_url = self.service.swift_temp_url(image_info=self.fake_image)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
CONF.glance.swift_endpoint_url + tempurl_mock.return_value,
|
||||||
|
temp_url)
|
||||||
|
tempurl_mock.assert_called_with(
|
||||||
|
path=path,
|
||||||
|
seconds=CONF.glance.swift_temp_url_duration,
|
||||||
|
key=CONF.glance.swift_temp_url_key,
|
||||||
|
method='GET')
|
||||||
|
|
||||||
|
@mock.patch('swiftclient.utils.generate_temp_url', autospec=True)
|
||||||
|
def test_swift_temp_url_radosgw_endpoint_invalid(self, tempurl_mock):
|
||||||
|
self.config(swift_endpoint_url='https://swift.radosgw.com/eggs/',
|
||||||
|
group='glance')
|
||||||
|
self.config(temp_url_endpoint_type='radosgw', group='glance')
|
||||||
|
self.service._validate_temp_url_config = mock.Mock()
|
||||||
|
|
||||||
|
self.assertRaises(exception.InvalidParameterValue,
|
||||||
|
self.service.swift_temp_url,
|
||||||
|
self.fake_image)
|
||||||
|
self.assertFalse(tempurl_mock.called)
|
||||||
|
|
||||||
@mock.patch('swiftclient.utils.generate_temp_url', autospec=True)
|
@mock.patch('swiftclient.utils.generate_temp_url', autospec=True)
|
||||||
def test_swift_temp_url_multiple_containers(self, tempurl_mock):
|
def test_swift_temp_url_multiple_containers(self, tempurl_mock):
|
||||||
|
|
||||||
@ -732,6 +792,11 @@ class TestGlanceSwiftTempURL(base.TestCase):
|
|||||||
self.assertRaises(exception.MissingParameterValue,
|
self.assertRaises(exception.MissingParameterValue,
|
||||||
self.service._validate_temp_url_config)
|
self.service._validate_temp_url_config)
|
||||||
|
|
||||||
|
def test__validate_temp_url_no_account_exception_radosgw(self):
|
||||||
|
self.config(swift_account=None, group='glance')
|
||||||
|
self.config(temp_url_endpoint_type='radosgw', group='glance')
|
||||||
|
self.service._validate_temp_url_config()
|
||||||
|
|
||||||
def test__validate_temp_url_endpoint_negative_duration(self):
|
def test__validate_temp_url_endpoint_negative_duration(self):
|
||||||
self.config(swift_temp_url_duration=-1,
|
self.config(swift_temp_url_duration=-1,
|
||||||
group='glance')
|
group='glance')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user