Merge "Config drive support for ceph radosgw"
This commit is contained in:
commit
19ced862a7
@ -64,6 +64,9 @@ Configure Ironic and Glance with RADOS Gateway
|
||||
swift_api_version = v1
|
||||
swift_endpoint_url = http://RADOS_IP:PORT
|
||||
swift_temp_url_key = TEMP_URL_KEY
|
||||
temp_url_endpoint_type=radosgw
|
||||
|
||||
[deploy]
|
||||
|
||||
object_store_endpoint_type = radosgw
|
||||
|
||||
#. Restart Ironic conductor service(s).
|
||||
|
@ -1089,11 +1089,8 @@
|
||||
# the check entirely. (integer value)
|
||||
#sync_local_state_interval = 180
|
||||
|
||||
# Whether to upload the config drive to Swift. (boolean value)
|
||||
#configdrive_use_swift = false
|
||||
|
||||
# Name of the Swift container to store config drive data. Used
|
||||
# when configdrive_use_swift is True. (string value)
|
||||
# when configdrive_use_object_store is True. (string value)
|
||||
#configdrive_swift_container = ironic_configdrive_container
|
||||
|
||||
# Timeout (seconds) for waiting for node inspection. 0 -
|
||||
@ -1396,6 +1393,18 @@
|
||||
# Allowed values: netboot, local
|
||||
#default_boot_option = <None>
|
||||
|
||||
# Whether to upload the config drive to object store. Set this
|
||||
# option to True to store config drive in swift or radosgw.
|
||||
# (boolean value)
|
||||
# Deprecated group/name - [conductor]/configdrive_use_swift
|
||||
#configdrive_use_object_store = false
|
||||
|
||||
# Type of object store endpoint type to be used as a backend
|
||||
# (string value)
|
||||
# Allowed values: swift, radosgw
|
||||
# Deprecated group/name - [glance]/temp_url_endpoint_type
|
||||
#object_store_endpoint_type = swift
|
||||
|
||||
|
||||
[dhcp]
|
||||
|
||||
@ -1645,12 +1654,6 @@
|
||||
# downloads. Required for temporary URLs. (string value)
|
||||
#swift_temp_url_key = <None>
|
||||
|
||||
# Type of endpoint to use for temporary URLs. If the Glance
|
||||
# backend is Swift, use "swift"; if it is CEPH with RADOS
|
||||
# gateway, use "radosgw". (string value)
|
||||
# Allowed values: swift, radosgw
|
||||
#temp_url_endpoint_type = swift
|
||||
|
||||
# Tenant ID (string value)
|
||||
#tenant_id = <None>
|
||||
|
||||
|
@ -58,6 +58,54 @@ for example::
|
||||
ironic node-set-provision-state --config-drive /dir/configdrive_files $node_identifier active
|
||||
|
||||
|
||||
Configuration drive storage in an object store
|
||||
----------------------------------------------
|
||||
|
||||
Under normal circumstances, the configuration drive can be stored in the
|
||||
Bare Metal service when the size is less than 64KB. Optionally, if the size
|
||||
is larger than 64KB there is support to store it in swift or radosgw backed
|
||||
object store. Both swift and radosgw use swift-style APIs.
|
||||
|
||||
The following option in ``/etc/ironic/ironic.conf`` enables swift as an object
|
||||
store backend to store config drive. This uses the Identity service to
|
||||
establish a session between the Bare Metal service and the
|
||||
Object Storage service. ::
|
||||
|
||||
[deploy]
|
||||
...
|
||||
|
||||
configdrive_use_object_store = True
|
||||
|
||||
Use the following options in ``/etc/ironic/ironic.conf`` to enable radosgw.
|
||||
Credentials in the swift section are needed because radosgw will not use the
|
||||
Identity service and relies on radosgw's username and password authentication
|
||||
instead. ::
|
||||
|
||||
[deploy]
|
||||
...
|
||||
|
||||
configdrive_use_object_store = True
|
||||
object_store_endpoint_type = radosgw
|
||||
|
||||
[swift]
|
||||
...
|
||||
|
||||
username = USERNAME
|
||||
password = PASSWORD
|
||||
auth_url = http://RADOSGW_IP:8000/auth/v1
|
||||
|
||||
Make sure that if an agent_* driver is being used, edit
|
||||
``/etc/glance/glance-api.conf`` to store the instance images in respective
|
||||
object store (radosgw or swift) as well::
|
||||
|
||||
[glance_store]
|
||||
...
|
||||
|
||||
swift_store_user = USERNAME
|
||||
swift_store_key = PASSWORD
|
||||
swift_store_auth_address = http://RADOSGW_OR_SWIFT_IP:PORT/auth/v1
|
||||
|
||||
|
||||
Accessing the configuration drive data
|
||||
--------------------------------------
|
||||
|
||||
@ -81,10 +129,13 @@ the configuration drive and mount it, for example::
|
||||
mount $CONFIG_DEV /mnt/config
|
||||
|
||||
|
||||
.. [*] A config drive could also be a data block with a VFAT filesystem
|
||||
.. [*] A configuration drive could also be a data block with a VFAT filesystem
|
||||
on it instead of ISO 9660. But it's unlikely that it would be needed
|
||||
since ISO 9660 is widely supported across operating systems.
|
||||
|
||||
For more information see `Store metadata on a configuration drive
|
||||
<http://docs.openstack.org/user-guide/cli-config-drive.html>`_.
|
||||
|
||||
|
||||
Cloud-init integration
|
||||
----------------------
|
||||
|
@ -147,7 +147,7 @@ class GlanceImageService(base_image_service.BaseImageService,
|
||||
}
|
||||
|
||||
endpoint_url = CONF.glance.swift_endpoint_url
|
||||
if CONF.glance.temp_url_endpoint_type == 'radosgw':
|
||||
if CONF.deploy.object_store_endpoint_type == 'radosgw':
|
||||
chunks = urlparse.urlsplit(CONF.glance.swift_endpoint_url)
|
||||
if not chunks.path:
|
||||
endpoint_url = urlparse.urljoin(
|
||||
@ -184,7 +184,7 @@ class GlanceImageService(base_image_service.BaseImageService,
|
||||
'Swift temporary URLs require a Swift endpoint URL. '
|
||||
'You must provide "swift_endpoint_url" as a config option.'))
|
||||
if (not CONF.glance.swift_account and
|
||||
CONF.glance.temp_url_endpoint_type == 'swift'):
|
||||
CONF.deploy.object_store_endpoint_type == 'swift'):
|
||||
raise exc.MissingParameterValue(_(
|
||||
'Swift temporary URLs require a Swift account string. '
|
||||
'You must provide "swift_account" as a config option.'))
|
||||
|
@ -23,7 +23,7 @@ from swiftclient import utils as swift_utils
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common import keystone
|
||||
|
||||
from ironic.conf import CONF
|
||||
|
||||
_SWIFT_SESSION = None
|
||||
|
||||
@ -39,8 +39,22 @@ class SwiftAPI(object):
|
||||
"""API for communicating with Swift."""
|
||||
|
||||
def __init__(self):
|
||||
session = _get_swift_session()
|
||||
self.connection = swift_client.Connection(session=session)
|
||||
"""Initialize the connection with swift or radosgw
|
||||
|
||||
:raises: ConfigInvalid if required keystone authorization credentials
|
||||
with swift are missing.
|
||||
"""
|
||||
params = {}
|
||||
if CONF.deploy.object_store_endpoint_type == 'radosgw':
|
||||
params = {'authurl': CONF.swift.auth_url,
|
||||
'user': CONF.swift.username,
|
||||
'key': CONF.swift.password}
|
||||
else:
|
||||
# NOTE(aNuposic): Session will be initiated only when connection
|
||||
# with swift is initialized. Since v3.2.0 swiftclient supports
|
||||
# instantiating the API client from keystoneauth session.
|
||||
params = {'session': _get_swift_session()}
|
||||
self.connection = swift_client.Connection(**params)
|
||||
|
||||
def create_object(self, container, obj, filename,
|
||||
object_headers=None):
|
||||
|
@ -85,6 +85,8 @@ class BaseConductorManager(object):
|
||||
:raises: DriverLoadError if an enabled driver cannot be loaded.
|
||||
:raises: DriverNameConflict if a classic driver and a dynamic driver
|
||||
are both enabled and have the same name.
|
||||
:raises: ConfigInvalid if required config options for connection with
|
||||
radosgw are missing while storing config drive.
|
||||
"""
|
||||
if self._started:
|
||||
raise RuntimeError(_('Attempt to start an already running '
|
||||
@ -172,6 +174,18 @@ class BaseConductorManager(object):
|
||||
self._periodic_task_callables,
|
||||
executor_factory=periodics.ExistingExecutor(self._executor))
|
||||
|
||||
# Check for required config options if object_store_endpoint_type is
|
||||
# radosgw
|
||||
if (CONF.deploy.configdrive_use_object_store and
|
||||
CONF.deploy.object_store_endpoint_type == "radosgw"):
|
||||
if (None in (CONF.swift.auth_url, CONF.swift.username,
|
||||
CONF.swift.password)):
|
||||
msg = _("Parameters missing to make a connection with "
|
||||
"radosgw. Ensure that [swift]/auth_url, "
|
||||
"[swift]/username, and [swift]/password are all "
|
||||
"configured.")
|
||||
raise exception.ConfigInvalid(msg)
|
||||
|
||||
# clear all target_power_state with locks by this conductor
|
||||
self.dbapi.clear_node_target_power_state(self.host)
|
||||
# clear all locks held by this conductor before registering
|
||||
|
@ -2695,17 +2695,20 @@ def _get_configdrive_obj_name(node):
|
||||
def _store_configdrive(node, configdrive):
|
||||
"""Handle the storage of the config drive.
|
||||
|
||||
If configured, the config drive data are uploaded to Swift. The Node's
|
||||
instance_info is updated to include either the temporary Swift URL
|
||||
from the upload, or if no upload, the actual config drive data.
|
||||
If configured, the config drive data are uploaded to swift or radosgw.
|
||||
The Node's instance_info is updated to include either the temporary
|
||||
Swift URL from the upload, or if no upload, the actual config drive data.
|
||||
|
||||
:param node: an Ironic node object.
|
||||
:param configdrive: A gzipped and base64 encoded configdrive.
|
||||
:raises: SwiftOperationError if an error occur when uploading the
|
||||
config drive to Swift.
|
||||
config drive to swift or radosgw.
|
||||
:raises: ConfigInvalid if required keystone authorization credentials
|
||||
with swift are missing.
|
||||
|
||||
|
||||
"""
|
||||
if CONF.conductor.configdrive_use_swift:
|
||||
if CONF.deploy.configdrive_use_object_store:
|
||||
# NOTE(lucasagomes): No reason to use a different timeout than
|
||||
# the one used for deploying the node
|
||||
timeout = CONF.conductor.deploy_callback_timeout
|
||||
|
@ -107,13 +107,11 @@ opts = [
|
||||
'conductor will check for nodes that it should '
|
||||
'"take over". Set it to a negative value to disable '
|
||||
'the check entirely.')),
|
||||
cfg.BoolOpt('configdrive_use_swift',
|
||||
default=False,
|
||||
help=_('Whether to upload the config drive to Swift.')),
|
||||
cfg.StrOpt('configdrive_swift_container',
|
||||
default='ironic_configdrive_container',
|
||||
help=_('Name of the Swift container to store config drive '
|
||||
'data. Used when configdrive_use_swift is True.')),
|
||||
'data. Used when configdrive_use_object_store is '
|
||||
'True.')),
|
||||
cfg.IntOpt('inspect_timeout',
|
||||
default=1800,
|
||||
help=_('Timeout (seconds) for waiting for node inspection. '
|
||||
|
@ -72,6 +72,20 @@ opts = [
|
||||
'default is "netboot", but it will be changed to '
|
||||
'"local" in the future. It is recommended to set '
|
||||
'an explicit value for this option.')),
|
||||
cfg.BoolOpt('configdrive_use_object_store',
|
||||
default=False,
|
||||
deprecated_group='conductor',
|
||||
deprecated_name='configdrive_use_swift',
|
||||
help=_('Whether to upload the config drive to object store. '
|
||||
'Set this option to True to store config drive '
|
||||
'in swift or radosgw.')),
|
||||
cfg.StrOpt('object_store_endpoint_type',
|
||||
default='swift',
|
||||
deprecated_group='glance',
|
||||
deprecated_name='temp_url_endpoint_type',
|
||||
choices=['swift', 'radosgw'],
|
||||
help=_('Type of object store endpoint type to be '
|
||||
'used as a backend')),
|
||||
]
|
||||
|
||||
|
||||
|
@ -103,12 +103,6 @@ opts = [
|
||||
'value between 1 and 32, a single-tenant store will use '
|
||||
'multiple containers to store images, and this value '
|
||||
'will determine how many containers are created.')),
|
||||
cfg.StrOpt('temp_url_endpoint_type',
|
||||
default='swift',
|
||||
choices=['swift', 'radosgw'],
|
||||
help=_('Type of endpoint to use for temporary URLs. If the '
|
||||
'Glance backend is Swift, use "swift"; if it is CEPH '
|
||||
'with RADOS gateway, use "radosgw".')),
|
||||
cfg.StrOpt('glance_host',
|
||||
default='$my_ip',
|
||||
help=_('Default glance hostname or IP address.')),
|
||||
|
@ -744,7 +744,7 @@ class TestGlanceSwiftTempURL(base.TestCase):
|
||||
|
||||
@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')
|
||||
self.config(object_store_endpoint_type='radosgw', group='deploy')
|
||||
path = ('/v1'
|
||||
'/glance'
|
||||
'/757274c4-2856-4bd2-bb20-9a4a231e187b')
|
||||
@ -769,7 +769,7 @@ class TestGlanceSwiftTempURL(base.TestCase):
|
||||
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')
|
||||
self.config(object_store_endpoint_type='radosgw', group='deploy')
|
||||
path = ('/v1'
|
||||
'/glance'
|
||||
'/757274c4-2856-4bd2-bb20-9a4a231e187b')
|
||||
@ -793,7 +793,7 @@ class TestGlanceSwiftTempURL(base.TestCase):
|
||||
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.config(object_store_endpoint_type='radosgw', group='deploy')
|
||||
self.service._validate_temp_url_config = mock.Mock()
|
||||
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
@ -851,7 +851,7 @@ class TestGlanceSwiftTempURL(base.TestCase):
|
||||
|
||||
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.config(object_store_endpoint_type='radosgw', group='deploy')
|
||||
self.service._validate_temp_url_config()
|
||||
|
||||
def test__validate_temp_url_endpoint_less_than_download_delay(self):
|
||||
|
@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
import six
|
||||
from six.moves import builtins as __builtin__
|
||||
from six.moves import http_client
|
||||
@ -24,6 +25,7 @@ from ironic.common import exception
|
||||
from ironic.common import swift
|
||||
from ironic.tests import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
if six.PY3:
|
||||
import io
|
||||
@ -39,10 +41,35 @@ class SwiftTestCase(base.TestCase):
|
||||
self.swift_exception = swift_exception.ClientException('', '')
|
||||
|
||||
def test___init__(self, connection_mock, keystone_mock):
|
||||
"""Check if client is properly initialized with swift"""
|
||||
|
||||
swift.SwiftAPI()
|
||||
connection_mock.assert_called_once_with(
|
||||
session=keystone_mock.return_value)
|
||||
|
||||
def test___init___radosgw(self, connection_mock, swift_session_mock):
|
||||
"""Check if client is properly initialized with radosgw"""
|
||||
|
||||
auth_url = 'http://1.2.3.4'
|
||||
username = 'foo'
|
||||
password = 'foo_password'
|
||||
CONF.set_override('object_store_endpoint_type', 'radosgw',
|
||||
group='deploy')
|
||||
opts = [cfg.StrOpt('auth_url'), cfg.StrOpt('username'),
|
||||
cfg.StrOpt('password')]
|
||||
CONF.register_opts(opts, group='swift')
|
||||
|
||||
CONF.set_override('auth_url', auth_url, group='swift')
|
||||
CONF.set_override('username', username, group='swift')
|
||||
CONF.set_override('password', password, group='swift')
|
||||
|
||||
swift.SwiftAPI()
|
||||
params = {'authurl': auth_url,
|
||||
'user': username,
|
||||
'key': password}
|
||||
connection_mock.assert_called_once_with(**params)
|
||||
swift_session_mock.assert_not_called()
|
||||
|
||||
@mock.patch.object(__builtin__, 'open', autospec=True)
|
||||
def test_create_object(self, open_mock, connection_mock, keystone_mock):
|
||||
swiftapi = swift.SwiftAPI()
|
||||
|
@ -250,6 +250,28 @@ class StartStopTestCase(mgr_utils.ServiceSetUpMixin, tests_db_base.DbTestCase):
|
||||
self.service.del_host()
|
||||
self.assertTrue(wait_mock.called)
|
||||
|
||||
def test_start_fails_on_missing_config_for_configdrive(self):
|
||||
"""Check to fail conductor on missing config options"""
|
||||
|
||||
missing_parameters_error = ("Parameters missing to make a "
|
||||
"connection with radosgw")
|
||||
CONF.set_override('configdrive_use_object_store', True,
|
||||
group='deploy')
|
||||
CONF.set_override('object_store_endpoint_type', 'radosgw',
|
||||
group='deploy')
|
||||
params = {'auth_url': 'http://1.2.3.4',
|
||||
'username': 'foo', 'password': 'foo_pass'}
|
||||
CONF.register_opts((cfg.StrOpt(x) for x in params),
|
||||
group='swift')
|
||||
for key, value in params.items():
|
||||
test_params = params.copy()
|
||||
test_params[key] = None
|
||||
for test_key, test_value in test_params.items():
|
||||
CONF.set_override(key, test_value, group='swift')
|
||||
with self.assertRaisesRegex(exception.ConfigInvalid,
|
||||
missing_parameters_error):
|
||||
self._start_service()
|
||||
|
||||
|
||||
class CheckInterfacesTestCase(mgr_utils.ServiceSetUpMixin,
|
||||
tests_db_base.DbTestCase):
|
||||
|
@ -1433,7 +1433,8 @@ class DoNodeDeployTearDownTestCase(mgr_utils.ServiceSetUpMixin,
|
||||
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy')
|
||||
def test__do_node_deploy_configdrive_swift_error(self, mock_deploy,
|
||||
mock_swift):
|
||||
CONF.set_override('configdrive_use_swift', True, group='conductor')
|
||||
CONF.set_override('configdrive_use_object_store', True,
|
||||
group='deploy')
|
||||
self._start_service()
|
||||
# test when driver.deploy.deploy returns DEPLOYDONE
|
||||
mock_deploy.return_value = states.DEPLOYDONE
|
||||
@ -5042,7 +5043,8 @@ class StoreConfigDriveTestCase(tests_base.TestCase):
|
||||
expected_instance_info = {'configdrive': 'http://1.2.3.4'}
|
||||
|
||||
# set configs and mocks
|
||||
CONF.set_override('configdrive_use_swift', True, group='conductor')
|
||||
CONF.set_override('configdrive_use_object_store', True,
|
||||
group='deploy')
|
||||
CONF.set_override('configdrive_swift_container', container_name,
|
||||
group='conductor')
|
||||
CONF.set_override('deploy_callback_timeout', timeout,
|
||||
|
@ -0,0 +1,23 @@
|
||||
---
|
||||
features:
|
||||
- Adds support for storing the configdrive in radosgw using
|
||||
the swift API.
|
||||
- |
|
||||
Adds support to use the radosgw authentication mechanism that relies
|
||||
on username and password instead of auth token.
|
||||
The following options must be specified in ironic configuration file:
|
||||
|
||||
* ``[swift]/auth_url``
|
||||
* ``[swift]/username``
|
||||
* ``[swift]/password``
|
||||
|
||||
deprecations:
|
||||
- The ``[conductor]/configdrive_use_swift`` and
|
||||
``[glance]/temp_url_endpoint_type`` options are deprecated and will be
|
||||
removed in the Queens release.
|
||||
Use ``[deploy]/configdrive_use_object_store`` and
|
||||
``[deploy]/object_store_endpoint_type`` respectively instead.
|
||||
upgrade:
|
||||
- Adds a ``[deploy]/object_store_endpoint_type`` option to specify the
|
||||
type of endpoint to use for instance images and configdrive storage.
|
||||
Allowed values are 'swift' or 'radosgw'. The default is 'swift'.
|
Loading…
Reference in New Issue
Block a user