encryptors: Introduce encryption provider constants

These constants detail the supported encryption formats and their
associated in tree encryption provider implementations.

The use of out of tree and direct use of these in tree implementations
is now deprecated and will be blocked in the 16.0.0 Pike release of
Nova.

Change-Id: Ic155bd29d46059832cce970bf60375e7e472eca6
Partial-bug: #1639293
This commit is contained in:
Lee Yarwood 2016-11-04 18:28:47 +00:00
parent 80306af88b
commit cbbc218282
2 changed files with 101 additions and 35 deletions

View File

@ -41,43 +41,57 @@ class VolumeEncryptorTestCase(test.NoDBTestCase):
}
self.encryptor = self._create(self.connection_info)
class VolumeEncryptorInitTestCase(VolumeEncryptorTestCase):
def setUp(self):
super(VolumeEncryptorInitTestCase, self).setUp()
def _test_get_encryptor(self, provider, expected_provider_class):
encryption = {'control_location': 'front-end',
'provider': provider}
encryptor = encryptors.get_volume_encryptor(self.connection_info,
**encryption)
self.assertIsInstance(encryptor, expected_provider_class)
def test_get_encryptors(self):
encryption = {'control_location': 'front-end',
'provider': 'LuksEncryptor'}
encryptor = encryptors.get_volume_encryptor(self.connection_info,
**encryption)
self.assertIsInstance(encryptor,
luks.LuksEncryptor,
"encryptor is not an instance of LuksEncryptor")
encryption = {'control_location': 'front-end',
'provider': 'CryptsetupEncryptor'}
encryptor = encryptors.get_volume_encryptor(self.connection_info,
**encryption)
self.assertIsInstance(encryptor,
cryptsetup.CryptsetupEncryptor,
"encryptor is not an instance of"
"CryptsetupEncryptor")
self._test_get_encryptor('luks',
luks.LuksEncryptor)
# TODO(lyarwood): Remove the following in 16.0.0 Pike
self._test_get_encryptor('LuksEncryptor',
luks.LuksEncryptor)
self._test_get_encryptor('nova.volume.encryptors.luks.LuksEncryptor',
luks.LuksEncryptor)
encryption = {'control_location': 'front-end',
'provider': 'NoOpEncryptor'}
encryptor = encryptors.get_volume_encryptor(self.connection_info,
**encryption)
self.assertIsInstance(encryptor,
nop.NoOpEncryptor,
"encryptor is not an instance of NoOpEncryptor")
self._test_get_encryptor('plain',
cryptsetup.CryptsetupEncryptor)
# TODO(lyarwood): Remove the following in 16.0.0 Pike
self._test_get_encryptor('CryptsetupEncryptor',
cryptsetup.CryptsetupEncryptor)
self._test_get_encryptor(
'nova.volume.encryptors.cryptsetup.CryptsetupEncryptor',
cryptsetup.CryptsetupEncryptor)
def test_get_error_encryptos(self):
self._test_get_encryptor(None,
nop.NoOpEncryptor)
# TODO(lyarwood): Remove the following in 16.0.0 Pike
self._test_get_encryptor('NoOpEncryptor',
nop.NoOpEncryptor)
self._test_get_encryptor('nova.volume.encryptors.nop.NoOpEncryptor',
nop.NoOpEncryptor)
def test_get_missing_encryptor_error(self):
encryption = {'control_location': 'front-end',
'provider': 'ErrorEncryptor'}
self.assertRaises(ValueError, encryptors.get_volume_encryptor,
self.connection_info, **encryption)
@mock.patch('nova.volume.encryptors.LOG')
def test_error_log(self, log):
encryption = {'control_location': 'front-end',
'provider': 'TestEncryptor'}
def test_get_missing_out_of_tree_encryptor_log(self, log):
provider = 'TestEncryptor'
encryption = {'control_location': 'front-end',
'provider': provider}
try:
encryptors.get_volume_encryptor(self.connection_info, **encryption)
except Exception as e:
@ -86,3 +100,29 @@ class VolumeEncryptorTestCase(test.NoDBTestCase):
"%(exception)s",
{'provider': provider,
'exception': e})
log.warning.assert_called_once_with("Use of the out of tree "
"encryptor class %(provider)s "
"will be blocked with the "
"16.0.0 Pike release of Nova.",
{'provider': provider})
@mock.patch('nova.volume.encryptors.LOG')
def test_get_direct_encryptor_log(self, log):
encryption = {'control_location': 'front-end',
'provider': 'LuksEncryptor'}
encryptors.get_volume_encryptor(self.connection_info, **encryption)
encryption = {'control_location': 'front-end',
'provider': 'nova.volume.encryptors.luks.LuksEncryptor'}
encryptors.get_volume_encryptor(self.connection_info, **encryption)
log.warning.assert_has_calls([
mock.call("Use of the in tree encryptor class %(provider)s by "
"directly referencing the implementation class will be "
"blocked in the 16.0.0 Pike release of Nova.",
{'provider': 'LuksEncryptor'}),
mock.call("Use of the in tree encryptor class %(provider)s by "
"directly referencing the implementation class will be "
"blocked in the 16.0.0 Pike release of Nova.",
{'provider':
'nova.volume.encryptors.luks.LuksEncryptor'})])

View File

@ -19,11 +19,27 @@ from oslo_utils import importutils
from oslo_utils import strutils
from nova.i18n import _LE, _LW
from nova.volume.encryptors import nop
LOG = logging.getLogger(__name__)
LUKS = "luks"
PLAIN = "plain"
FORMAT_TO_FRONTEND_ENCRYPTOR_MAP = {
LUKS: 'nova.volume.encryptors.luks.LuksEncryptor',
PLAIN: 'nova.volume.encryptors.cryptsetup.CryptsetupEncryptor'
}
LEGACY_PROVIDER_CLASS_TO_FORMAT_MAP = {
"nova.volume.encryptors.luks.LuksEncryptor": LUKS,
"nova.volume.encryptors.cryptsetup.CryptsetupEncryptor": PLAIN,
"nova.volume.encryptors.nop.NoopEncryptor": None,
"LuksEncryptor": LUKS,
"CryptsetupEncryptor": PLAIN,
"NoOpEncryptor": None,
}
def get_volume_encryptor(connection_info, **kwargs):
"""Creates a VolumeEncryptor used to encrypt the specified volume.
@ -31,18 +47,28 @@ def get_volume_encryptor(connection_info, **kwargs):
:param: the connection information used to attach the volume
:returns VolumeEncryptor: the VolumeEncryptor for the volume
"""
encryptor = nop.NoOpEncryptor(connection_info, **kwargs)
location = kwargs.get('control_location', None)
if location and location.lower() == 'front-end': # case insensitive
provider = kwargs.get('provider')
if provider == 'LuksEncryptor':
provider = 'nova.volume.encryptors.luks.' + provider
elif provider == 'CryptsetupEncryptor':
provider = 'nova.volume.encryptors.cryptsetup.' + provider
elif provider == 'NoOpEncryptor':
provider = 'nova.volume.encryptors.nop.' + provider
# TODO(lyarwood): Remove the following in 16.0.0 Pike and raise an
# ERROR if provider is not a key in SUPPORTED_ENCRYPTION_PROVIDERS.
# Until then continue to allow both the class name and path to be used.
if provider in LEGACY_PROVIDER_CLASS_TO_FORMAT_MAP:
LOG.warning(_LW("Use of the in tree encryptor class %(provider)s"
" by directly referencing the implementation class"
" will be blocked in the 16.0.0 Pike release of "
"Nova."), {'provider': provider})
provider = LEGACY_PROVIDER_CLASS_TO_FORMAT_MAP[provider]
if provider in FORMAT_TO_FRONTEND_ENCRYPTOR_MAP:
provider = FORMAT_TO_FRONTEND_ENCRYPTOR_MAP[provider]
elif provider is None:
provider = "nova.volume.encryptors.nop.NoOpEncryptor"
else:
LOG.warning(_LW("Use of the out of tree encryptor class "
"%(provider)s will be blocked with the 16.0.0 "
"Pike release of Nova."), {'provider': provider})
try:
encryptor = importutils.import_object(provider, connection_info,
**kwargs)